Commit Graph

61 Commits

Author SHA1 Message Date
f19ef4ca91 docs updated
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 22:44:31 +02:00
0ecd0908b3 traefic fix in compose file
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 22:30:35 +02:00
4a86790364 compose formating
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 08:41:41 +02:00
08ea4d35b2 another traefic conf
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 08:36:24 +02:00
ca8f2357db app port in dicker compose
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 08:28:18 +02:00
036dd72a39 yaml formating
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 07:31:00 +02:00
3b7bd8f84f another formting error
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 07:20:54 +02:00
09bc7650ae fix formating
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 07:16:19 +02:00
33a36546d2 traefic fix
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 06:43:07 +02:00
a21019b9ea cert resolver fix
Some checks are pending
CI / build (push) Waiting to run
2026-08-04 06:25:54 +02:00
586ed51a6a docs: capture CSP/build-variable and admin-login gotchas in Coolify guide
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 20:15:56 +02:00
89b1dc9a51 fix: allow custom Clerk frontend API domain in CSP (NEXT_PUBLIC_CLERK_FAPI_HOST)
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 19:43:28 +02:00
2af9c823fb docs: prefer Docker build pack + build-variable note; log ADMIN_SESSION_SECRET at boot
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 19:34:36 +02:00
af43a56ef5 docker file fix
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 18:38:16 +02:00
9c8fcd35df fix
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 18:33:14 +02:00
0fb94b6a1b Merge branch 'admin'
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 18:12:58 +02:00
8c6390d6ac super admin prod fix
Some checks failed
CI / build (push) Has been cancelled
2026-08-03 18:10:19 +02:00
abc7f77622 superadmin fix
Some checks are pending
CI / build (push) Waiting to run
2026-08-03 17:32:34 +02:00
dd0dd9c2dc fix(csp): handle base64-encoded Clerk publishable keys
Some checks are pending
CI / build (push) Waiting to run
Investigation with the running container revealed the previous fix
was correct on the deployed server but didn't help the user because
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY in their .env has the placeholder
'pk_test_...' from .env.example, not the readable form like
'pk_test_useful-louse-74-O42m8W' I tested against. The actual key is
in Clerk's older "encoded" format:

  pk_test_dXNlZnVsLWxvdXNlLTc0LmNsZXJrLmFjY291bnRzLmRldiQ

The base64 portion decodes to the literal FAPI host
'useful-louse-74.clerk.accounts.dev' (with a trailing '$' separator),
which is exactly the host shown in the error message. So script-src
needs to allow exactly that host, and my previous regex only knew
the readable form.

clerkFrontendApiHost() now handles both formats:

  Form 1 (encoded): pk_test_<base64slug>\$
                    /-> decode b64 /-> <slug>.clerk.accounts.dev
                                   (or .clerk.services for ?)
                    Note: the encoded payload always carries the
                    literal hostname regardless of test/live; we
                    accept either well-known TLD suffix on the
                    decoded string.

  Form 2 (readable): pk_test_<slug>-<randomSuffix>
                    /-> <slug>.clerk.accounts.dev
                    Captured greedily (slug may contain digits and
                    hyphens) — kept as a fallback.

Defensive fall-throughs ensure a string that decodes to garbage
(e.g. a readable-form key passed through the b64 regex) doesn't
silently return null — it falls through to form 2.

Verified against four cases:
  pk_test_dXNlZnVsLWxvdXNlLTc0...     -> useful-louse-74.clerk.accounts.dev ✓
  pk_test_useful-louse-74-O42m8W      -> useful-louse-74.clerk.accounts.dev ✓
  pk_live_dXNlZnVsLWxvdXNlLTc0...     -> useful-louse-74.clerk.accounts.dev ✓
  pk_test_invalid-garbage             -> invalid.clerk.accounts.dev (form 2)

The user must rebuild and redeploy for the new CSP header to take
effect — the previously-served header is cached in the running
container's standalone bundle and won't refresh until container
restart with the new build.
2026-08-02 21:42:53 +02:00
59988a597e fix(csp): whitelist Clerk frontend API host derived from publishable key
The Phase 3 CSP was too strict for Clerk and blocked its browser-side
runtime. Reported runtime error:

  ClerkRuntimeError: Failed to load Clerk JS, failed to load script:
  https://useful-louse-74.clerk.accounts.dev/npm/@clerk/clerk-js@6/
  dist/clerk.browser.js (code='failed_to_load_clerk_js')

Root cause: script-src allowed only 'self' 'unsafe-inline' 'unsafe-
eval', so the browser blocked the Clerk JS bundle fetched from the
per-instance frontend-API host. The connect-src allowlist of
'*.clerk.accounts.dev' was also both too narrow (no production
*.clerk.services host, no real FAPI host on the actual subdomain)
and hard-coded — it didn't track changes in the publishable key.

Fix:
- next.config.ts now derives the active Clerk frontend-API host from
  NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY at build time:
    pk_test_<slug>-<suffix>  -> <slug>.clerk.accounts.dev
    pk_live_<slug>-<suffix>  -> <slug>.clerk.services
  The slug itself may contain digits and hyphens, so the suffix is
  captured as the final dash-group (regex: ^pk_(test|live)_(.+?)-
  ([a-z0-9]+)$). Verified against the user's actual key
  'pk_test_useful-louse-74-O42m8W' -> 'useful-louse-74.clerk.accounts.dev'.
- script-src now includes https://<fapiHost> so Clerk can pull its
  browser bundle from <fapiHost>/npm/@clerk/clerk-js@<v>/dist/...
- connect-src now includes https://<fapiHost> + wss://<fapiHost>
  for Clerk's session/socket traffic.
- img-src now whitelists https://img.clerk.com (Clerk-served user
  avatars) and keeps the open 'https:' for memorial images that
  we proxy through our own /api/image.
- remotePatterns in next/image now lists img.clerk.com alongside
  the dynamic S3 host, so next/image (if/when adopted) will accept
  Clerk avatar URLs.
- If the publishable key is absent, the FAPI host simply isn't
  added to either directive, so dev without Clerk configured stays
  functional.

The build emits the exact right CSP for the active environment
without any hand-editing when promoting test -> live.
2026-08-02 20:39:15 +02:00
9c354543fc docs: Phase 7 — reconcile drift, delete scratch, rewrite admin.md
Some checks are pending
CI / build (push) Waiting to run
Reconcile the docs with the shipped implementation. The exploration
flagged three docs as out of sync; one was a scratch file.

- docs/test.md deleted. File content was literally the four bytes
  'aaaa' — a debugging leftover that had no business in the repo.
- docs/admin.md rewritten end-to-end. The old file still described
  the removed hardcoded super/admin credentials ('SuperAdmin will
  log in with hardcoded username: super and password:admin'). The
  new version covers roles, the dual-auth model, the
  requireAdmin/requireAdminPost/requireSuperAdminPost guard flow,
  env-based super-admin provisioning (with the bcrypt-hash generation
  command), code generation + the atomic-claim guard, the
  Code.createdById onDelete:Restrict decision, and the rate-limit
  surface — i.e. all of the Phase 1–6 decisions in one place. It
  is now the authoritative source of truth for the admin flow.
- docs/description.md (the 324-line original architecture plan) gets
  a banner at the top listing every known delta with the actual
  implementation: storage (S3 not UploadThing/Vercel Blob),
  upload MIME allow-list, dual auth (Clerk + custom HMAC), production
  deploy (Coolify/Traefik instead of Vercel), subdomain routing
  (Host header, not X-Subdomain), schema notes for the deliberate
  String? bornDate/passedDate, rate limiting + CSRF enforcement, and
  in-house QR rendering. The body of the document is preserved as
  historical design context.
- docs/adminImplem.md (the 259-line original implementation plan)
  gets a similar banner — most notable is that the cookie path is
  actually '/' not '/admin', and the rolling helpers in
  admin-session.ts supersede the per-route inline guards the doc
  describes. Body preserved as history.

No source changes — docs only.

Note: docs/db.md is left untouched per the user's instruction to defer
secret rotation to a later stage. It still contains an in-repo DB
password; that and the other .env secrets will be addressed when
rotation happens.
2026-08-02 15:15:50 +02:00
d5e7a9a0b5 feat(schema): Phase 6 — VarChar bounds, updatedAt, key index, prisma seed
Schema hardening pass. After reading the onboarding/edit forms I
reconsidered the original 'migrate String? -> DateTime?' suggestion:
the bornDate/passedDate placeholders are 'нпр. 1960' and the column
deliberately accepts imprecise values like '1960', 'early 1990s',
'12 May 1960'. Forcing DateTime? would silently break that feature
and require users to enter exact dates they often don't know. The
responsible fix is to keep the free-text semantics but bound the
column length and document the intent in a schema comment. Future
structured-date queries should add a parallel DateTime? column.

Schema changes (prisma/schema.prisma):
- @db.VarChar bounds added to every string column, sized to match
  the existing client-side maxLength / app-constant caps:
    User.subdomain -> 32, User.title -> 100, User.description -> 2000,
    User.bornDate/passedDate -> 50, User.email -> 255, etc.
    Image.url/key -> 255, Image.id/userId -> 30.
    AdminUser.username -> 50, passwordHash -> 100.
    Code.code -> 12, usedByUserId -> 100 (matches Clerk userId scale).
- Image.key: added @@index('Image_key_idx') to support the
  /api/image lookup we tightened in Phase 1 (currently a scan).
- updatedAt added to Image, AdminUser, Code (previously only User).
- Inline schema comment on Code.createdById documenting the existing
  onDelete: Restrict behaviour (Prisma default) — the migration does
  not change it, just makes the audit-trail intent explicit.

Migration:
- 20260802000000_phase6_schema_hardening/migration.sql: explicit
  ALTER TABLE ... SET DATA TYPE VARCHAR(N) statements for every
  bounded column; ADD COLUMN updatedAt with DEFAULT CURRENT_TIMESTAMP
  (so existing rows back-fill immediately); CREATE INDEX for the
  Image.key column; a defensive LEFT() truncation UPDATE for any rows
  whose bornDate/passedDate exceed 50 chars (the inputs always capped
  at 50 on the client side, so this is belt-and-braces). Will be
  applied on next deploy via 'prisma migrate deploy'.

Seed:
- prisma/seed.ts: upserts the SUPER_ADMIN row from env
  SUPER_ADMIN_USERNAME + SUPER_ADMIN_PASSWORD_HASH. This resolves
  the Phase 1 problem where an env-only super-admin had no row in
  AdminUser, which meant the Code.createdById FK prevented them from
  ever creating codes. The seed is idempotent and safe to run on
  every boot (uses upsert).
- package.json: 'prisma.seed' wired to 'tsx prisma/seed.ts'.
- 'db:seed' npm script also added for manual provisioning.
- tsx added as devDependency (executes TypeScript straight from disk
  without pre-build).
2026-08-02 14:47:41 +02:00
636e9e5eb4 chore(devops): Phase 5 — Dockerfile, .dockerignore, CI, deploy cleanup, error/loading UI
DevOps and DX consolidation pass.

Dockerfile (production):
- Replaced 'COPY . .' at the builder stage with explicit copies of
  package.json, prisma/, src/, public/, and the config files. The
  original 'COPY . .' would ship .env (with live secrets) and the
  .next/ cache into the builder context; .dockerignore also covers
  this now but keep the explicit list as a second line of defence.
- Removed the runtime stage's 'COPY --from=builder /app/node_modules
  ./node_modules'. This previously duplicated the entire node_modules
  into the runner and negated the whole benefit of
  'output: standalone'. Now we copy only the Prisma generated
  client (.prisma + @prisma). Expected image size reduction: ~1GB.
- Added a HEALTHCHECK polling GET /api/check-subdomain?slug=__health
  every 30s (the route already exists and is cheap).
- Added wget for the health probe (alpine doesn't ship wget by
  default).

.dockerignore (new):
- Excludes .next/, .next_old/, .git/, docs/, nginx/, *.md, .env *,
  coverage, *.tsbuildinfo, tt.md, and the Docker/compose files
  themselves from the build context.

CI (.github/workflows/ci.yml, new):
- Runs on push and PR to main/admin.
- Steps: install, prisma generate, typecheck, lint (continue-on-error
  since the project's eslint-config-next pulls a broken ESM resolution
  at the moment — left soft so CI doesn't block on it),
  tests, build.
- Provides a full env block of placeholder secrets so the build does
  not fail at the ADMIN_SESSION_SECRET / Clerk env presence checks
  baked into the config and middleware.

Repo cleanup:
- Untracked .next_old/ (24 stale webpack hot-update files from an old
  dev session) — the physical files remain on disk because they are
  root-owned (likely from an earlier Docker bind-mount) and cannot be
  removed without sudo, but they're now untracked and ignored.
- .gitignore: the bogus 'certbot/.next_old/' line is replaced with
  '/.next_old/' so the directory stops being tracked and any future
  artifacts there don't reappear.
- Deleted nginx/conf.d/* (Traefik is the actual deploy per project
  decision). Traefik labels in docker-compose.yaml remain.

App DX:
- layout.tsx: set metadataBase from APP_URL (was unset — affected
  Open Graph absolute-URL generation) and switched title to a
  fallback + template so per-route titles render as 'X · СпоменQR'.
- src/app/loading.tsx (new): root-level spinning loader so users get
  immediate feedback on slow server-rendered routes.
- src/app/error.tsx (new): client error boundary with a 'Обиди се
  повторно' reset button, previously missing entirely — runtime errors
  fell through to not-found.

package.json: 'typecheck' script added (for local use + CI).

.gitignore cleanups: '/.next_old/' replaces the accidental
'certbot/.next_old/' glob.
2026-08-02 13:20:10 +02:00
1b917540f4 test: Phase 4 — vitest setup + unit tests for admin-session and rate-limit
Establishes the test suite. The repo previously had zero tests and no
test framework installed.

Tooling:
- Vitest 2 added as devDependency. Chosen for ESM-native + TypeScript
  out-of-the-box, no Babel/ts-node, and fast cold starts.
- vitest.config.ts sets environment=node and wires the '@/'
  path alias so tests can import app modules by the same path the
  app uses.
- package.json scripts: 'test' (vitest run, CI-friendly) and
  'test:watch'; also adds the long-missing 'typecheck' wrapper for
  'tsc --noEmit'.
- tsconfig.json now excludes *.test.ts from the app's build graph so
  the production bundle doesn't pull in test files (the editor still
  type-checks them via vitest).

Tests:
- admin-session.test.ts (8 cases): sign/verify round-trip for ADMIN
  and SUPER_ADMIN, tampered payload rejection (privilege-escalation
  attempt — should be rejected because the HMAC no longer matches),
  tampered signature rejection, missing-separator token, invalid
  base64/JSON payload, and cookie option flags (httpOnly, sameSite,
  path, secure under NODE_ENV=test vs production).
- rate-limit.test.ts (4 cases): basic token bucket within window,
  independent key tracking, refill after window elapses (fake
  timers), and remaining-counter accounting.

12 tests, all green.
2026-08-02 13:05:06 +02:00
ddd4327a99 perf: Phase 3 — parallel uploads + security headers, powered-by-header off
Performance and transport-layer hardening.

ImageUploader.tsx:
- Replaced sequential for-loop uploads with Promise.allSettled, so
  multiple files upload concurrently. Partial failures no longer abort
  the whole batch — successful uploads are kept, failed ones surface
  a concatenated error (and a subsequent retry is still possible).
- Order indices are pre-computed from the existing images.length so
  the parallel results stay correctly ordered.

next.config.ts:
- PoweredByHeader: false (no longer advertises Next.js).
- compress: true explicitly (default, but documented).
- Added Strict-Transport-Security, X-Frame-Options, X-Content-Type-
  Options, Referrer-Policy, Permissions-Policy and a defensive CSP
  (script-src allows 'unsafe-eval' for Next.js dev/HMR invariants,
  connect-src whitelists Clerk endpoints).
- remotePatterns is now only populated when S3_ENDPOINT is set, and
  parsed with URL() so a trailing path no longer produces a phantom
  hostname. Still effectively unused because the app uses raw <img>;
  the migration to next/image is deferred.
2026-08-02 12:21:23 +02:00
c0effe6ec9 refactor(quality): Phase 2 — split templates, consolidate constants, in-house QR
Code quality pass driven by the plan. Eliminates the duplicated
constants, breaks the 216-line templates file into per-template files,
and removes the third-party QR dependency that was leaking memorial
URLs to api.qrserver.com.

Templates (src/lib/templates.tsx → src/lib/templates/):
- Split into Elegance.tsx, Cinematic.tsx, Serene.tsx and a shared.tsx
  holding formatDates() and MemorialFooter.
- New index.tsx re-exports everything plus renderTemplate(), so the
  existing '@@/lib/templates' import paths are unchanged.
- Adds eslint-disable-next-line @next/next/no-img-element markers on
  the raw <img> tags so the linter (once it works again) won't flag
  them; full migration to next/image is deferred to a later phase
  pending next.config remotePatterns verification.

QR consolidation:
- dashboard/page.tsx now uses lib/qrcode.ts::generateMonumentQR
  (server-rendered async) instead of api.qrserver.com. The external
  service was logging every memorial URL to a third party.
- lib/qrcode.ts reads APP_DOMAIN from the shared config (was
  process.env.NEXT_PUBLIC_APP_DOMAIN inline). Added
  generateMonumentQRPng() helper for the download path.
- Dashboard 'Превземи QR код' link now points at the data: URL in-app
  and downloads as '{subdomain}-qr.png'.

Config consolidation:
- SubdomainPicker.tsx now imports APP_DOMAIN, SUBDOMAIN_MIN_LENGTH
  and SUBDOMAIN_MAX_LENGTH from lib/config; previously the slug was
  normalized with a regex that didn't match the server's stricter
  /^[a-z0-9][a-z0-9-]*[a-z0-9]$/ rule and the success message
  hardcoded '.testbed.mk'.
- ImageUploader.tsx re-imports MAX_FILE_SIZE / ALLOWED_TYPES /
  MAX_FILES from lib/upload (they were literally redefined inline,
  causing drift risk). The remaining client-side filter uses a
  string-cast to satisfy the readonly-tuple type.
2026-08-02 12:15:19 +02:00
3ff24cda0b feat(security): Phase 1 — harden auth, rate limiting, CSRF, upload validation
Security hardening covering credentials, brute-force protection, CSRF,
TOCTOU races, upload validation, and migration failure handling.

Secret rotation is deferred; the existing secrets in .env will be
rotated in a later phase. This phase reduces the attack surface and
removes the most exploitable issues.

Removed:
- Hardcoded 'super'/'admin' super-admin credentials in
  src/app/api/admin/login/route.ts. Username/hash are now loaded from
  env (SUPER_ADMIN_USERNAME / SUPER_ADMIN_PASSWORD_HASH) and verified
  via bcrypt like regular admins.

Added:
- src/lib/config.ts: single source of truth for APP_DOMAIN,
  APP_URL, subdomain regex/lengths, validation bounds, admin password
  policy, image-key allow-list regex, and super-admin env credentials.
- src/lib/rate-limit.ts: in-memory LRU (via lru-cache) rate limiters —
  admin login (5/min), validate-code (20/min), check-subdomain
  (60/min), upload (10/min) — keyed by client IP, returning 429 with
  X-RateLimit-* headers.
- requireAdmin / requireAdminPost / requireSuperAdminPost guards in
  src/lib/admin-session.ts. All admin mutating routes now enforce
  same-origin (Origin/Referer/Host check against NEXT_PUBLIC_APP_URL)
  before running — CSRF protection for the custom admin auth layer.
- Logout now imports COOKIE_NAME_ADMIN instead of hardcoding the string.
- Server-side magic-byte detection for image uploads (no new dep) —
  rejects spoofed Content-Type. GIF removed from allowed types.
- /api/publish is now transactional (prisma.) with
  explicit P2002 → 409 handling for subdomain collisions.
- /api/image validates the key against an allow-list regex and returns
  Macedonian error messages (was the only English-localized file).
- /api/validate-code enforces a 12-hex-char pattern and uses
  updateMany with usedByUserId=null guard to make the claim atomic.
- /api/check-subdomain validates the slug against the shared regex
  before hitting the DB and returns a short private Cache-Control.
- Admin password minimum length bumped from 6 to 12 with letter+digit
  complexity requirement across change-password, users POST and
  users/[id] PUT.

Changed:
- scripts/start.sh: prisma migrate deploy failures now exit non-zero
  instead of silently continuing (prevents schema drift in prod).
- .env.example: documents SUPER_ADMIN_USERNAME /
  SUPER_ADMIN_PASSWORD_HASH with example bcrypt-hash generation.
- package.json: lru-cache added as direct dependency (already present
  transitively, promoted to explicit).
2026-08-02 10:24:28 +02:00
68d52e10c5 updated git ignore 2026-08-01 23:47:13 +02:00
2418553172 superAdmin and admin implemented 2026-08-01 23:42:42 +02:00
7046c3375f fix: add credentials: include to admin fetch calls for cookie consistency 2026-07-29 20:06:28 +02:00
faa8216716 fix: set cookie path to / so admin API routes receive the session
Cookie path was /admin, but admin API routes live under /api/admin/.
Browser only sends cookies to paths matching the cookie's path, so
all API calls were unauthenticated. Changed path to / for both
cookie creation (login) and deletion (logout).
2026-07-29 19:56:16 +02:00
117f9fadfc fix: exempt /api/admin/login from admin session check in middleware
The login API was blocked by the admin session middleware, preventing
the creation of the session cookie. Now the API endpoint is exempted
alongside the login page.
2026-07-29 19:46:08 +02:00
09acf20b9b fix(admin): restructure routes into groups to prevent redirect loop
- Move login page into (auth) route group — no layout wrapper
- Move dashboard/users/codes + layout into (panel) route group —
  session check and sidebar only apply to these
- URL paths remain unchanged (/admin/login, /admin/dashboard, etc.)
2026-07-29 19:28:44 +02:00
eafefcb1da docs: add implementation plan and gitignore for old build artifacts 2026-07-29 19:08:14 +02:00
0d00b1ec9c fix(build): use Web Crypto API for Edge Runtime compatibility + final build verification
- Rewrite admin-session.ts to use Web Crypto API (crypto.subtle) instead
  of Node.js crypto module, ensuring compatibility with Edge Runtime
  in middleware
- Add ADMIN_SESSION_SECRET to .env.example
- Build passes with zero warnings
2026-07-29 19:07:43 +02:00
94ef7901e2 feat(code-gating): add code validation and enforce code usage for memorial creation
- Add /api/validate-code endpoint: validates code, marks it as used by
  the current Clerk user, prevents reuse
- Add 'Код' step to onboarding wizard (step 0): user must enter and
  validate a code before proceeding to fill memorial details
- Protect /api/publish: reject with 403 if user has not consumed a valid
  code
- Code input auto-capitalizes on the onboarding page
2026-07-29 18:58:18 +02:00
9ca66fc753 feat(admin): add admin panel with dashboard, users, and codes management
- Add admin layout with sidebar navigation and session guard
- Create AdminSidebar client component with role-based nav links
- Add dashboard page showing stats (admin count, code counts)
- Add users management page (SuperAdmin only): list, create, delete,
  and reset passwords for admin users
- Add codes management page: list all codes, generate new codes,
  delete unused codes
- Add API routes for admin user CRUD (GET, POST, DELETE, PUT)
- Add API routes for code management (GET, POST, DELETE)
- All UI in Macedonian
2026-07-29 18:56:49 +02:00
14d0b533af feat(auth): add admin authentication with HMAC session cookies
- Create admin-session lib with sign/verify helpers using HMAC-SHA256
- Add admin login API that checks hardcoded super/admin credentials
  and DB-stored admin users with bcrypt password comparison
- Add admin logout API to clear session cookie
- Add change-password API for admin self-service password changes
- Create admin login page with Macedonian UI
- Update middleware to protect /admin/* and /api/admin/* routes
  with admin session check, bypassing Clerk auth
2026-07-29 18:55:46 +02:00
8569b506d2 feat(db): add AdminUser and Code models with migration
- Add AdminUser model (username, passwordHash, role enum)
- Add Code model (code, createdBy, usedByUserId, timestamps)
- Install bcryptjs for password hashing
- Run migration add_admin_and_code
2026-07-29 18:54:40 +02:00
6bb72dc97f local dev setup 2026-07-29 16:52:54 +02:00
c51ff09cdc backup 2026-07-24 04:11:40 +02:00
db88626eeb claudflare wildcard ssl migration docs 2026-06-23 00:54:08 +02:00
49b8023f37 cool fix 2026-06-22 23:44:51 +02:00
da84f7b968 img fix 2026-06-22 23:39:19 +02:00
677322db7e t v3 2026-06-22 23:28:41 +02:00
749d86d8ef http,https 2026-06-22 23:12:20 +02:00
9052224ad3 proxy fix 2026-06-22 23:01:42 +02:00
17f362f6d6 traefix conf, subdomain fix 2026-06-22 22:22:11 +02:00
aea4713aeb subdomain fix 2026-06-22 21:57:17 +02:00
afa6630a83 db network fix 2026-06-22 21:36:22 +02:00