5.3 KiB
5.3 KiB
Shorty — Agent-oriented project guide
This file describes the repository layout, conventions, and workflows so LLM agents and humans can work in it accurately and efficiently.
What this repo is
- Shorty is a self-hosted stack: Kutt (URL shortener) + QR Designer (qr-api + qr-web).
- Kutt: short links at
mifi.me, admin atlink.mifi.me(Postgres + Redis). - QR Designer: Next.js app at
qr.mifi.dev(BasicAuth); backend qr-api (Express, SQLite, uploads, shorten proxy to Kutt). qr-api is internal-only (backend network).
Repo layout (monorepo, pnpm workspace)
shorty/
├── package.json # Root: scripts lint, format, format:check, test, build (all delegate to workspaces)
├── pnpm-workspace.yaml # packages: qr-api, qr-web
├── docker-compose.yml # Full stack with build (Kutt + qr-api + qr-web from Dockerfiles)
├── docker-compose.portainer.yml # Same stack using registry images; for Portainer + webhook redeploy
├── .woodpecker/
│ ├── ci.yml # CI: install → format → lint → test → build
│ └── deploy.yml # Deploy: buildx qr-api + qr-web (multi-arch), push, Portainer webhook
├── qr-api/ # Express API (TS), SQLite, multer uploads, Kutt proxy
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── src/
│ └── package.json
└── qr-web/ # Next.js 15 (App Router), Mantine, qr-code-styling
├── Dockerfile
├── .dockerignore
├── next.config.ts # output: 'standalone'
├── src/
└── package.json
- No root Dockerfile. Images are built from
qr-api/andqr-web/only. - Lockfile:
pnpm-lock.yamlis committed; CI usespnpm install --frozen-lockfile.
Key scripts (from repo root)
| Command | Effect |
|---|---|
pnpm install |
Install deps for all workspaces |
pnpm run lint |
ESLint in qr-api and qr-web |
pnpm run format:check |
Prettier check (no write) |
pnpm run format |
Prettier write |
pnpm run test |
Vitest in qr-api and qr-web |
pnpm run build |
Build qr-api (tsc) and qr-web (next build) |
Per-package: pnpm --filter qr-api dev, pnpm --filter qr-web dev (dev servers).
Environment and config
- Root:
.env.exampledocuments vars; copy to.envfor Docker Compose..envis gitignored. - Devcontainer:
.devcontainer/devcontainer.jsonsetsDB_PATH,UPLOADS_PATH,KUTT_API_KEY,QR_API_URLfor in-container dev; no.envrequired for qr-api/qr-web dev. - Compose: Kutt needs
DB_PASSWORD,JWT_SECRET; qr-api optionallyKUTT_API_KEY. Portainer stack can setREGISTRY,IMAGE_TAGfor registry-based compose.
CI/CD (Woodpecker)
- CI (runs on PR, push to main, tag, manual): Node 22, pnpm, then
format:check→lint→test→build. No Docker in CI. - Deploy (runs on main push/tag/manual after CI): Uses
docker buildxto build two images from./qr-apiand./qr-webwith--platform linux/amd64,linux/arm64, tags:latestand:${CI_COMMIT_SHA}, pushes togit.mifi.dev/mifi-holdings/shorty-qr-apiandshorty-qr-web, then POSTsportainer_webhook_urlto trigger Portainer stack redeploy. - Secrets:
gitea_registry_username,gitea_package_token,portainer_webhook_url.
Portainer (production)
- Registry-based stack: Use
docker-compose.portainer.yml. Images:${REGISTRY}/mifi-holdings/shorty-qr-api:${IMAGE_TAG}, same forshorty-qr-web. Add stack webhook in Portainer and set that URL asportainer_webhook_urlin Woodpecker. - Build-from-source: Use
docker-compose.yml; no registry, no webhook.
Code style and tooling
- TypeScript only (qr-api and qr-web).
- Prettier: 4 spaces, single quotes, trailing comma all, semicolons (see
.prettierrc). Check withpnpm run format:check. - ESLint: Root
.eslintrc.cjsfor qr-api; qr-web has its own.eslintrc.cjs(Next + Prettier). No TSLint. - Ignores:
.gitignore(e.g. node_modules, .pnpm-store, .next, dist, .env, *.tsbuildinfo);.prettierignoreand ESLintignorePatternsaligned (coverage, build dirs, .pnpm-store). Each app has.dockerignoreto keep build context small.
Where to change what
- API routes (qr-api):
qr-api/src/routes/,qr-api/src/index.ts. - Web app (qr-web):
qr-web/src/app/(App Router),qr-web/src/components/,qr-web/src/contexts/. - Shared types: Defined in each package; no shared package.
- Compose: Kutt + qr services in
docker-compose.yml; registry-only variant indocker-compose.portainer.yml. - Pipelines:
.woodpecker/ci.yml,.woodpecker/deploy.yml. Image names and registry are indeploy.ymlenv.
Testing and building locally
- Unit tests:
pnpm run test(Vitest in both packages). - Dev:
pnpm --filter qr-api devandpnpm --filter qr-web dev(ports 8080 and 3000); devcontainer forwards them. - Full stack (Docker):
docker compose up -d(usesdocker-compose.yml; needs.envwithDB_PASSWORD,JWT_SECRET). - Multi-arch build (local): Use
docker buildx build --platform linux/amd64,linux/arm64 -t <repo>:<tag> --push ./qr-api(and same for./qr-web) after logging into the registry.
This layout and these scripts are the source of truth for automation and agent-driven edits.