An open-source backbone that ships the entire academic publishing loop — submit, review, publish, read — in one codebase.
11 backend modules · 644 tests at 84% coverage · 66 end-to-end specs · strict typing end to end
Why · Screenshots · What's inside · Architecture · Quick start · Testing · Docs · Contributing
Most teams rebuild the same journal scaffold from scratch — submission forms, reviewer assignment, a CMS for published papers. ScholarHUB ships that scaffold as a real, multi-role product instead of yet another custom CMS:
- One platform, four roles. Authors submit; editors assign and decide; reviewers report; readers browse, read, and follow. No glue code between disconnected systems.
- The full loop, not a demo. Manuscript metadata, single/double-blind review, versioned revisions, DOI registration, catalog, in-browser reading with cross-device progress, subscriptions, and recommendations — all wired together.
- Secure by default. Passkeys (WebAuthn) and TOTP two-factor, JWT with a server-side denylist and key rotation, captcha on signup, and a per-action audit log.
- Self-hostable in minutes.
docker compose upon a single node; PostgreSQL for production, SQLite for dev and CI.
Real captures from the running app — desktop 1440×900 · mobile 390×844. Click the hero image for the 60-second tour.
![]() Catalog — faceted search over published work |
![]() Resource detail — metadata, DOI, abstract, files |
![]() Reader — in-browser PDF with synced progress |
![]() Author — submissions, revisions and status |
![]() Editor — assign reviewers, decide, publish |
![]() Reviewer — read manuscripts, file reports |
![]() Recommendations — ranked by your reading history |
![]() Library — reading lists, cross-device progress |
![]() Admin — users, roles and audit log |
| Capability | Highlights |
|---|---|
| Submissions & review | Full metadata intake, single/double-blind workflows, reviewer assignment, versioned revisions, editor decisions, terminal-state guards |
| Publication & catalog | Volume/issue management, searchable published catalog, DOI registration via DataCite |
| Metadata ingest | Pull authoritative records from Crossref, arXiv, PubMed, OpenAlex, and Semantic Scholar — plus BibTeX / RIS / CSV import |
| Reader experience | In-browser PDF reader, reading-progress sync across devices, personal reading lists, follow authors & subjects |
| Auth & security | WebAuthn passkeys, TOTP 2FA, JWT denylist + key rotation, captcha, RBAC (author / editor / reviewer / reader / admin) |
| Multi-tenant | Host multiple journals on one deployment with host-based tenant resolution and cached routing |
| Discovery | Follow graphs, recommendations, email + in-app notifications, citation export (BibTeX / RIS / CSL) |
Every domain capability is an independent module — disable, replace, or extend it without touching core.
- Backend — FastAPI (async), SQLAlchemy 2.0 async, PostgreSQL / SQLite, modular
app/modules/*with strictmypyandruff. - Frontend — React 19 + TanStack Router + TypeScript 5.9 + Tailwind v4 + shadcn/ui, type-safe end to end.
- Tests —
pytest(parallel, 84% line coverage,--cov-fail-under=80),vitestfor the frontend, Playwright for the full submit → review → publish → read journey.
- Two-layer tenant isolation. Every domain table carries a
tenant_id. The app appends the filter on every query, and PostgreSQL Row-Level Security rejects cross-tenant rows even if the app forgets — defense in depth, not a hope. - Module registry.
app.core.modules.load_all()loads modules in dependency order, registers their ORM tables, mounts their routes, and adds health checks. New capability = one entry, zero core changes.
# 1. Generate strong secrets
echo "SCHOLARHUB_SECRET_KEY=$(openssl rand -hex 32)" > .env
echo "SCHOLARHUB_ADMIN_PASSWORD=$(openssl rand -base64 18)" >> .env
# 2. Start the dev stack (Postgres + backend + frontend)
docker compose -f infra/docker-compose.yml up --build
# 3. Open the API docs and the SPA
xdg-open http://localhost:8000/docs
xdg-open http://localhost:5173Requires Python 3.12+, Node 20+, and a PostgreSQL 17 instance.
# Backend
cd apps/backend && uv sync && uv run alembic upgrade head
uv run uvicorn app.main:app --reload
# Frontend (another terminal)
cd apps/frontend && npm install && npm run devcp .env .env.prod # fill at least SCHOLARHUB_SECRET_KEY + SCHOLARHUB_ADMIN_PASSWORD
# edit infra/Caddyfile -> replace scholarhub.example.com with your domain
docker compose -f infra/docker-compose.prod.yml --env-file .env.prod up -d --buildMail (Mailgun / SendGrid / SES / Postmark) and OIDC SSO (Google / GitHub / Keycloak): see integrations.md.
Every choice is mainstream and long-term hostable — no exotic dependencies.
| Layer | Backend | Frontend |
|---|---|---|
| Language / framework | Python 3.12+, FastAPI 0.115+ | React 19, TypeScript 5.9, Vite 7 |
| Data | SQLAlchemy 2 (async), Alembic, PostgreSQL 17 | TanStack Router v1, TanStack Query v5, Zustand |
| Validation / auth | Pydantic 2, JWT + bcrypt, PyJWT, authlib (OIDC) | shadcn/ui + Radix, Tailwind v4, lucide-react |
| Infra | Docker Compose, Caddy (auto TLS), structlog | Playwright (E2E) |
| Toolchain | uv, ruff, mypy (strict), pytest, bandit | ESLint, Vitest, tsc project references |
All variables are prefixed SCHOLARHUB_. The full list and the .env template live in apps/backend/app/core/config.py and apps/backend/.env.example. Essentials: SCHOLARHUB_SECRET_KEY, SCHOLARHUB_ADMIN_PASSWORD, SCHOLARHUB_DATABASE_URL, SCHOLARHUB_TENANCY_MODE (single / multi), SCHOLARHUB_ENVIRONMENT.
Defense in depth is enabled the moment the backend boots:
- Auth — bcrypt hashing; short-lived JWT access + httpOnly refresh cookie + per-user
token_version. - 2FA (TOTP) — RFC 6238, per-user secret Fernet-encrypted at rest, 10 single-use backup codes (SHA-256).
- Passkeys — WebAuthn registration / authentication state machine with one-time, TTL-bound challenges.
- JWT key rotation — ordered key chain;
POST /api/admin/reload-secret-keysrotates with zero downtime. - Rate limit — sliding window per IP + route;
RedisRateLimiterStorewhenSCHOLARHUB_REDIS_URLis set, otherwise in-memory (Redis errors auto-fail-open). - GDPR — export / soft-delete (30-day grace) / restore self-service endpoints.
- Headers & errors — CSP, HSTS, CSRF double-submit; RFC 7807
application/problem+jsoneverywhere; per-tenant audit log on every privileged action.
See SECURITY.md for the full policy and threat model.
core creates these on startup (assignable from the admin shell):
| Role | Scope |
|---|---|
admin |
Full access — admin shell, user management, audit log |
editor |
Assign reviewers, organize volumes/issues, accept/reject, push to published |
reviewer |
View assigned submissions, file review reports |
author |
Submit manuscripts, view own status, upload revisions |
member |
Read, save, follow, view recommendations |
Quality is enforced in CI, not just claimed:
- Backend — 644
pytestcases at 84% line coverage with a hard--cov-fail-under=80gate;mypy --strictandruffclean. - Frontend —
vitestunit + component tests under stricttsc(100 cases). - E2E — 66 Playwright specs exercising the real submit → review → publish → read workflow against a spawned test server (no flaky production parity).
- CI — backend, frontend, and e2e jobs on every push; strict pytest markers; a version-consistency guard keeps
VERSION/pyproject/package.json/__version__in lockstep.
# Backend
cd apps/backend && uv run ruff check . && uv run mypy app && uv run pytest -q
# Frontend
cd apps/frontend && npm run lint && npm run typecheck && npm run test
# E2E (Playwright spawns both servers via E2E_SPAWN_SERVER=1)
cd apps/frontend && E2E_SPAWN_SERVER=1 npx playwright test- Architecture · Deployment · Integrations
- Contributing · Security · Code of Conduct · Support · Changelog
Issues and PRs are welcome — see CONTRIBUTING.md for branch naming, commit conventions, and the PR checklist.
Four platforms in parallel (same branches, tags, and HEAD) — no favorites, pick any one:
| Platform | URL |
|---|---|
| GitHub | https://github.com/x33834/scholarhub |
| GitHub | https://github.com/Morningstar202604/scholarhub |
| GitCode | https://gitcode.com/badhope/scholarhub |
| Gitee | https://gitee.com/badhope/scholarhub |
Official sites (GitHub Pages, both accounts, identical): https://x33834.github.io/scholarhub/ · https://morningstar202604.github.io/scholarhub/
Copyright © 2026 Morningstar202604. Released under the Apache-2.0 License. Provided "as is", without warranty of any kind.








