docs: add implementation plan and gitignore for old build artifacts

This commit is contained in:
dimitar 2026-07-29 19:08:14 +02:00
parent 0d00b1ec9c
commit eafefcb1da
2 changed files with 260 additions and 1 deletions

2
.gitignore vendored
View File

@ -36,4 +36,4 @@ yarn-error.log*
next-env.d.ts next-env.d.ts
# docker # docker
certbot/ certbot/.next_old/

259
adminImplem.md Normal file
View File

@ -0,0 +1,259 @@
# SuperAdmin & Admin + Code Access System — Implementation Plan
## Overview
Implement SuperAdmin/admin role management and code-gated memorial creation as outlined in `admin.md`.
- SuperAdmin: hardcoded username `super`, password `admin`
- SuperAdmin creates admin accounts
- Admins generate access codes
- Users must enter a valid code during onboarding to create memories
---
## Phase 1 — Database & Dependencies
### New Prisma Models (`prisma/schema.prisma`)
```prisma
enum Role {
SUPER_ADMIN
ADMIN
}
model AdminUser {
id String @id @default(cuid())
username String @unique
passwordHash String
role Role @default(ADMIN)
createdAt DateTime @default(now())
createdCodes Code[]
}
model Code {
id String @id @default(cuid())
code String @unique
createdById String
createdBy AdminUser @relation(fields: [createdById], references: [id])
usedByUserId String? // User.clerkId
usedAt DateTime?
createdAt DateTime @default(now())
expiresAt DateTime?
@@index([code])
@@index([usedByUserId])
}
```
### New Dependencies
- `bcryptjs` + `@types/bcryptjs` — hash admin passwords
### Seed SuperAdmin
Seed script or migration that creates the SuperAdmin `AdminUser` record with hashed password. However, the login itself checks hardcoded `super`/`admin` first (and also queries DB by role for token auth), so the seed is optional — used mainly for listing in the admin panel.
### Migration
`npx prisma migrate dev --name add_admin_and_code`
---
## Phase 2 — Admin Auth
Admin auth is separate from Clerk. Uses a signed HMAC cookie.
### `src/lib/admin-session.ts` (new)
Helpers:
- `createAdminSession(username: string, role: Role): string` — sign a cookie value with HMAC-SHA256 using `ADMIN_SESSION_SECRET` env var
- `verifyAdminSession(token: string): { username: string; role: Role } | null` — verify and decode
- `getAdminSession(): { username: string; role: Role } | null` — read from `request.cookies` or `cookies()`
- Cookie name: `admin_session`
Payload: `{ username, role, iat }` serialized + HMAC signature.
### `src/app/api/admin/login/route.ts` (new)
- POST: accept `{ username, password }`
- If `username === "super"` and `password === "admin"` → set session with role `SUPER_ADMIN`
- Else query `AdminUser` where `username === username`, compare with `bcrypt.compare`
- Return `{ success: true }` and set `admin_session` cookie (httpOnly, secure, sameSite=lax, path=/admin)
- On failure: `401`
### `src/app/api/admin/logout/route.ts` (new)
- POST: clear `admin_session` cookie
### `src/app/api/admin/change-password/route.ts` (new)
- POST: accept `{ currentPassword, newPassword }`
- Verify admin session, then verify current password against DB
- Hash new password, update `AdminUser` record
### `src/app/admin/login/page.tsx` (new)
- Macedonian UI: username/password form
- On submit → `POST /api/admin/login`
- On success → redirect to `/admin/dashboard`
- Show error on failure
### `src/middleware.ts` (modify)
- Add `/admin(.*)` and `/api/admin(.*)` to the Clerk exclude list
- Before Clerk middleware runs: if path starts with `/admin`, check admin session cookie
- No/invalid cookie → redirect to `/admin/login`
- Valid → allow
- If path starts with `/api/admin`, check admin session cookie
- No/invalid cookie → return `401`
---
## Phase 3 — Admin Panel
All UI in Macedonian. Layout with sidebar navigation.
### `src/app/admin/layout.tsx` (new)
- Checks admin session (server component)
- Redirects to `/admin/login` if not authenticated
- Provides sidebar with links: Dashboard, Users (SuperAdmin only), Codes
- Logout button
### `src/app/admin/dashboard/page.tsx` (new)
- Stats cards:
- Total AdminUsers count
- Total Codes generated
- Codes used vs unused
- Simple overview
### `src/app/admin/users/page.tsx` (new)
- Accessible only to `SUPER_ADMIN`
- Table of admin users (username, role, created at)
- Button to create new admin (modal/page with username + password fields)
- Button to delete admin (with confirmation)
- Inline password reset option
### `src/app/admin/users/create/page.tsx` or modal (new)
- Form: username, password (with confirmation)
- POST to `/api/admin/users/`
### `src/app/admin/codes/page.tsx` (new)
- "Generate Code" button → POST `/api/admin/codes` → returns new code string, displays it
- Table of all codes: code value, who created it, status (used/unused), used by, used at
- Admin can see their own codes. SuperAdmin sees all.
- Delete code button
### `src/app/api/admin/users/route.ts` (new)
- GET: list all `AdminUser` (SuperAdmin only)
- POST: create `AdminUser` with hashed password (SuperAdmin only)
### `src/app/api/admin/users/[id]/route.ts` (new)
- DELETE: remove `AdminUser` (SuperAdmin only)
- PUT: reset password (SuperAdmin only)
### `src/app/api/admin/codes/route.ts` (new)
- GET: list codes (Admins see their own, SuperAdmin sees all)
- POST: generate a new code (`crypto.randomBytes(6).toString('hex').toUpperCase()` → 12 chars), store with `createdById` from session
### `src/app/api/admin/codes/[id]/route.ts` (new)
- DELETE: remove unused code
---
## Phase 4 — Code Gating
### `src/app/api/validate-code/route.ts` (new)
- Requires Clerk auth (`auth()` from `@clerk/nextjs/server`)
- POST: `{ code: string }`
- Look up `Code` where `code === code` and `usedByUserId === null`
- If not found → `{ valid: false, error: "Невалиден или веќе искористен код" }`
- If found → update `usedByUserId = userId`, `usedAt = now()`
- Return `{ valid: true }`
### `src/app/onboarding/page.tsx` (modify)
- Add `"Код"` as step 0 (before "Податоци")
- STEPS becomes: `["Код", "Податоци", "Датуми", "Фотографии", "Поддомен", "Шаблон"]`
- Step 0: single input for code + "Потврди" button
- Calls `POST /api/validate-code`
- On success → shows green checkmark, enables "Продолжи"
- On failure → shows error, blocks progression
- Once validated, user can proceed to step 1
- `canProceed()` for step 0 returns true only if code validated successfully
### `src/app/api/publish/route.ts` (modify)
- After Clerk auth check, query: `prisma.code.findFirst({ where: { usedByUserId: userId } })`
- If no code found → return `403` with `"Потребен е валиден код за креирање спомен страница"`
---
## Phase 5 — Environment & Build
### New Env Variable
```
ADMIN_SESSION_SECRET=<random-64-char-string>
```
Add to `.env.example` and `docker-compose` files.
### Build & Verify
```bash
npm install bcryptjs
npm install -D @types/bcryptjs
npx prisma migrate dev --name add_admin_and_code
npm run build
```
Verify:
- [ ] Admin login at `/admin/login` with `super`/`admin`
- [ ] Create admin user from admin panel
- [ ] Login as created admin
- [ ] Generate code from admin panel
- [ ] Sign up as new user via Clerk
- [ ] Enter code in onboarding step 0
- [ ] Proceed through onboarding and publish
- [ ] Verify publish fails without valid code
---
## File Change Summary
### New Files
| Path | Purpose |
|------|---------|
| `src/lib/admin-session.ts` | HMAC cookie helpers |
| `src/app/admin/login/page.tsx` | Admin login form |
| `src/app/admin/layout.tsx` | Admin layout with sidebar |
| `src/app/admin/dashboard/page.tsx` | Dashboard stats |
| `src/app/admin/users/page.tsx` | Admin user management |
| `src/app/admin/codes/page.tsx` | Code generation & listing |
| `src/app/api/admin/login/route.ts` | Admin login API |
| `src/app/api/admin/logout/route.ts` | Admin logout API |
| `src/app/api/admin/change-password/route.ts` | Self-service password change |
| `src/app/api/admin/users/route.ts` | List/create admin users |
| `src/app/api/admin/users/[id]/route.ts` | Delete/reset-password admin user |
| `src/app/api/admin/codes/route.ts` | List/create codes |
| `src/app/api/admin/codes/[id]/route.ts` | Delete code |
| `src/app/api/validate-code/route.ts` | Public code validation |
### Modified Files
| Path | Change |
|------|--------|
| `prisma/schema.prisma` | Add `AdminUser` and `Code` models |
| `src/middleware.ts` | Exclude `/admin/*` from Clerk, add admin session check |
| `src/app/onboarding/page.tsx` | Add code step (step 0) |
| `src/app/api/publish/route.ts` | Add code usage check before publish |