Aran Roig 68b74eb60b
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 24s
Graph calculator improvmentes
2026-10-02 14:53:00 +02:00
2026-10-01 21:36:30 +02:00
2026-10-02 14:53:00 +02:00
2026-09-30 01:49:35 +02:00
2026-10-02 14:53:00 +02:00
2026-09-30 01:49:35 +02:00
2026-10-01 21:34:56 +02:00

Mathew — a wiki for mathematics

A collaborative wiki of mathematical concepts. Articles are written in Markdown with LaTeX (KaTeX), stored in MongoDB behind an Express API, and rendered by a Nuxt 4 app as an open book with a table of contents, a table of topics, a link graph, and a review-then-publish flow for edits.

The interface speaks five languages — English, Spanish, Catalan, French, German — chosen per account, and articles themselves are multilingual: one article can exist in several languages, each with its own address.

mathew/
├── dev.sh              # start/stop both dev servers together
├── docker-compose.yml  # nginx + frontend + backend (prebuilt images)
├── nginx.conf          # reverse proxy: / → frontend, /api/ → backend
├── backend/            # Express 5 + Mongoose API  (port 5000)
└── frontend/           # Nuxt 4 / Vue 3 wiki UI    (port 3000)

Quick start

Requires MongoDB on localhost:27017 (database mathew) and Node 20+.

# 1. API
cd backend
npm install
cp .env.example .env      # then set JWT_SECRET
npm run seed              # optional: sample articles with LaTeX
npm run dev               # → http://localhost:5000

# 2. UI
cd ../frontend
npm install
npm run dev               # → http://localhost:3000

Or run both together from the repo root:

./dev.sh                      # both dev servers in the foreground (Ctrl+C stops both)
./dev.sh start|stop|restart|status
./dev.sh logs [all|backend|frontend]

Scripts

Where Command What it does
root ./dev.sh [dev] Both dev servers, foreground, tagged output, one Ctrl+C stops both
backend/ npm run dev API with node --watch (no build step)
backend/ npm start API without watch
backend/ npm run seed A handful of sample articles
backend/ npm run seed:bulk 100 cross-linked articles for load-testing the UI and graph (idempotent)
frontend/ npm run dev / build / preview Nuxt dev server, production build, preview of the build

There is no test suite, linter, or typecheck script configured.

Configuration

Where Variable Default Meaning
backend/.env PORT 5000 API port
backend/.env MONGO_URI mongodb://127.0.0.1:27017/mathew Mongo connection
backend/.env JWT_SECRET (insecure dev key) Signs session tokens — set this in production
backend/.env AUTH_TOKEN_TTL 7d How long a session lasts
frontend env NUXT_PUBLIC_API_BASE http://localhost:5000 Where the UI calls the API

How it works

Reading is open; writing takes an account. Anyone can register, and the first account ever created becomes the admin (on a database whose accounts all predate roles, the oldest one gets it at startup). The admin hands the role on or takes it back from /admin; nobody may change their own role, so the dashboard always has a door.

  • An admin's save applies immediately.
  • A member's save is answered 202 and filed as an edit proposal instead — the live article does not move. Saving again while a proposal waits revises it, and each proposal keeps a snapshot of the article as it read when the request was sent, so the desk can show a diff and warn when the page has moved since.
  • Proposals include new articles: a request for a page the wiki does not have yet keeps the address it asks for and appears only when approved.
  • Approving or rejecting happens at /admin/proposals, and approval runs the same code an admin's direct write runs — so a retitle re-slugs, and a language the article already has in another version is refused.
  • Deleting is admin-only, and it settles that article's waiting proposals with it.

Articles name no author, so they outlive the account that wrote them.

Languages

Two language ideas, deliberately apart:

  • The interface language (en, es, ca, fr, de) is a preference and never part of a URL. Precedence: the signed-in account's choice → the mathew-locale cookie → the browser → English. Pick it in the account popup.
  • The article language appears in the address: /wiki/<lang>/<identity>. Every version of an article has its own address, English included — /wiki/en/bayes-theorem and /wiki/fr/bayes-theorem are one article two ways. The short form /wiki/<slug> redirects onto the version the reader should see: their own language if the article speaks it, English failing that.

Switching an article's language turns the page of the open book rather than opening a second one, and "recently viewed" remembers article + language as a single entry.

Themes

Light and dark, plus eight accent colours, chosen in the settings popup and applied before first paint so there is no flash. Colours are CSS custom properties swapped by data-theme / data-accent on <html>.

Pages

Route What it is
/ Search, recently viewed, a way into the shelf
/articles The shelf, filed by topic — one folder per topic, opening onto a batch-at-a-time grid of cards; language and topic filters are applied by the API
/graph The wiki's link map: one node per article, one edge per linked pair
/wiki/<lang>/<slug> One language version of an article, drawn as a page of the book with its table of contents
/wiki/<slug> The short form, naming no language — turned onto the version the slug speaks
/wiki/<slug>/edit Markdown editor with live preview (Write / Split / Preview)
/new Create a new article
/admin Accounts and roles (admin only)
/admin/proposals The edit-request desk: diff, approve, reject (admin only)

Writing articles

GitHub-ish Markdown — headings, tables, lists, blockquotes, fenced code with syntax highlighting — plus LaTeX: $E = mc^2$ inline, $$ … $$ in display mode. Number-set macros \R, \N, \Z, \Q, \C are available. Raw HTML in an article is escaped, and everything rendered is sanitized with DOMPurify.

Link between articles with wiki links — [[Bayes' theorem]], [[Fourier series#definition]], [[Zeno's paradoxes|Zeno]] — or with a normal Markdown link to /wiki/<slug> or /wiki/<lang>/<slug>. Both feed the graph view.

Shortcuts: / focuses search on the home page, Ctrl/Cmd+K opens the search popup, Ctrl/Cmd+S saves in the editor. Changing an article's title can change its slug — old links then 404, as no redirects are kept.

API

GET /health answers { status, mongo }. Everything below lives under /api. Reads are public; writes need Authorization: Bearer <token> from /auth/login or /auth/register. All errors answer { message }.

Accounts

Method Endpoint Notes
POST /auth/register Creates the account and returns its session; the first account ever is an admin
POST /auth/login A wrong name and a wrong password answer the same way
GET /auth/me Who a token belongs to
PUT /auth/me Rename
PUT /auth/password Needs the current password
PUT /auth/locale Interface language — one of en es ca fr de
PUT /auth/avatar A small data URL; "" clears it

Articles

Articles are addressed by slug, not by Mongo id.

Method Endpoint Notes
GET /articles { articles, total, hasMore }, paged by article — ?q=, ?tag=, ?lang=, ?limit=, ?offset=; ?prefer= picks which version stands for an article, ?preferFirst ranks those versions first; no limit means the whole shelf
GET /articles/facets { total, topics, languages } counted over the whole shelf
GET /articles/random One random article
GET /articles/graph { nodes, edges } — the link map built from wiki and Markdown links
GET /articles/slug/:slug One article
POST /articles Create (title required; the slug is generated and uniquified). Admin applies; a member is answered 202 { proposal }
PUT /articles/slug/:slug Update. Admin applies; a member is answered 202 { proposal }
DELETE /articles/slug/:slug Admin only

Administration

Everything behind requireAuth, requireAdmin.

Method Endpoint Notes
GET /admin/users Every account, newest first
PUT /admin/users/:id/role role is admin or member; never your own
DELETE /admin/users/:id Close an account — never your own; its articles stay
GET /admin/proposals Every request, decided ones included, with its diff material
GET /admin/proposals/count Just { pending } — what the sidebar's badge wears
PUT /admin/proposals/:id/approve Writes the change; a conflict refuses it and the request keeps waiting
PUT /admin/proposals/:id/reject Refuses; the page stays put

/api/items is a small demo resource (CRUD over one schema) left in as a reference for how a router, model and error path fit together.

Deploy

docker-compose.yml runs nginx in front of prebuilt frontend and backend images: nginx listens on 3000 and routes /api/ to the backend and everything else to the Nuxt server, passing the standard forwarded headers. Point NUXT_PUBLIC_API_BASE and MONGO_URI at the deployment's real addresses and set a real JWT_SECRET — the fallback key is a development convenience only, and anyone holding it can mint a session.

Notes for contributors

AGENTS.md is the working map of the codebase: file-by-file layout, the backend and frontend conventions, and the gotchas that are easy to get wrong (article identity vs. document slug, versioned addresses, MongoDB's reserved language field, localization rules).

License

MIT.

Description
No description provided
Readme 1.3 MiB
Languages
Vue 42.1%
JavaScript 29.1%
SCSS 16%
TypeScript 11.8%
Shell 0.8%
Other 0.2%