Skip to content

Dev#11

Merged
Jophkins merged 22 commits intomainfrom
dev
Feb 9, 2026
Merged

Dev#11
Jophkins merged 22 commits intomainfrom
dev

Conversation

@Jophkins
Copy link
Owner

@Jophkins Jophkins commented Feb 9, 2026

No description provided.

Jophkins and others added 21 commits December 8, 2025 20:00
🚧 header and footer base versions
🚧 info-page added, minor fixes
🚧 added fake masonry gallery, api route, mock pictures
🚧 modal picture component in early stage
* 🚧 passed picture props, push escape to close modal

* 🚧 modal component layout update, proper placement

* 🚧 modal responsible, layout fixes
- Remove agents.md from .gitignore so it stays tracked
- Add typecheck and prisma convenience scripts to package.json
- Add ESLint override to allow process.env in lib/env/**
- Create lib/env/server.ts with zod validation for all server env vars

Co-authored-by: Cursor <cursoragent@cursor.com>
- Install prisma 7, @prisma/adapter-pg, next-auth, zod, aws-sdk, exifr, bcryptjs
- Create prisma schema with Photo model (EXIF fields, soft-delete, indexes)
  and NextAuth models (User, Account, Session, VerificationToken)
- Create prisma.config.ts for Prisma 7 migration adapter
- Create lib/db/prisma.ts singleton with PrismaPg adapter
- Add .env.example with all required server environment variables
- Extend ESLint env override to cover lib/db/** and prisma/**
- Add prisma/generated/ to .gitignore, allow .env.example

Co-authored-by: Cursor <cursoragent@cursor.com>
- Configure NextAuth v5 (beta) with Credentials provider for single-user admin
- Auth validates against ADMIN_USERNAME / ADMIN_PASSWORD_HASH from env
- Use JWT session strategy (no DB sessions needed for single-user)
- Add middleware to protect /admin/** routes with login redirect
- Create lib/auth/require-auth.ts helper for write API route protection
- Add /admin/login page with credentials form
- Add /admin dashboard placeholder (session-gated)
- Add /api/auth/[...nextauth] catch-all route handler

Co-authored-by: Cursor <cursoragent@cursor.com>
- Create S3-compatible client for DigitalOcean Spaces (lib/storage/client.ts)
- Add signed PUT URL generation with immutable UUID-based keys
- Keys follow pattern: original/<uuid>.<ext>, preview/<uuid>.webp, thumb/<uuid>.webp
- Originals are private ACL, preview/thumb are public-read
- Update next.config.ts with remotePatterns for Spaces CDN domains

Co-authored-by: Cursor <cursoragent@cursor.com>
- POST /api/uploads/sign: generates signed PUT URLs for original/preview/thumb
  variants with immutable UUID-based keys (auth-gated)
- POST /api/photos/commit: persists photo metadata and EXIF fields to DB
  after successful upload (auth-gated, GPS intentionally excluded)
- Add zod schemas in lib/validations/ for upload and photo inputs
- No storage secrets exposed in API responses

Co-authored-by: Cursor <cursoragent@cursor.com>
- Create lib/client/extract-exif.ts: extracts safe EXIF fields via exifr
  (GPS explicitly disabled per policy)
- Create lib/client/image-resize.ts: generates preview (~1600px) and
  thumb (~400px) WebP variants in the browser using OffscreenCanvas
- Add /admin/upload page with full upload flow:
  select files -> extract EXIF -> resize -> request signed URLs ->
  PUT to storage -> commit metadata to DB
- Supports batch upload with per-file status indicators

Co-authored-by: Cursor <cursoragent@cursor.com>
- Rewrite /api/pictures to query Postgres via Prisma
  (published + not soft-deleted, cursor-based pagination by ID)
- Build public image URLs from STORAGE_PUBLIC_BASE_URL + key
- Update gallery page: string cursor, extended Picture type with metadata
- Update modal-pic to display preview URL, title, description, and EXIF info
  (camera, lens, focal length, aperture, shutter, ISO)
- Remove lib/mock-pics.ts (no longer needed)

Co-authored-by: Cursor <cursoragent@cursor.com>
- GET /api/photos: admin-only list with filter (all/published/draft/deleted)
- PATCH /api/photos/:id: update metadata and publish state (zod-validated)
- POST /api/photos/:id/delete: soft-delete (sets deletedAt timestamp)
- POST /api/photos/:id/restore: restore soft-deleted photo (clears deletedAt)
- Add /admin/photos page with grid view, status badges, hover actions
  (publish/unpublish, delete, restore), and filter tabs
- Public gallery already filters by isPublished=true and deletedAt=null

Co-authored-by: Cursor <cursoragent@cursor.com>
- Rewrite README with setup instructions, env vars, scripts, CORS config,
  architecture overview, and deployment checklist
- Add decision log to agents.md documenting all MVP architectural choices:
  Prisma 7 adapter, NextAuth Credentials, ESLint env policy, browser-side
  image processing, EXIF GPS exclusion, cursor pagination, and Spaces config

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aed6285807

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +73 to +78
// 4. Upload files directly to storage
await Promise.all([
fetch(originalItem.url, { method: "PUT", body: entry.file, headers: { "Content-Type": entry.file.type } }),
fetch(previewItem.url, { method: "PUT", body: preview, headers: { "Content-Type": "image/webp" } }),
fetch(thumbItem.url, { method: "PUT", body: thumb, headers: { "Content-Type": "image/webp" } }),
]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify PUT uploads before committing metadata

The three fetch(..., { method: "PUT" }) calls are awaited via Promise.all, but their HTTP statuses are never checked. fetch resolves on 403/4xx/5xx, so a failed upload (expired signed URL, CORS rejection, storage outage) will still proceed to the commit request and create DB rows that point to missing objects, breaking the gallery until manually cleaned up. Please verify each response is ok (or throw on failure) before calling /api/photos/commit.

Useful? React with 👍 / 👎.

Comment on lines +19 to +24
orderBy: { createdAt: "desc" },
take: PAGE_SIZE + 1, // fetch one extra to check if there's a next page
...(cursorParam
? {
cursor: { id: cursorParam },
skip: 1, // skip the cursor item itself

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a stable cursor order for pagination

Pagination uses cursor: { id: cursorParam } while ordering by createdAt only. When multiple photos share the same createdAt (e.g., batch uploads), the ordering is non-deterministic and the cursor’s position can shift between pages, causing duplicates or missed items. A stable order (e.g., orderBy: [{ createdAt: "desc" }, { id: "desc" }] with a matching cursor/tie-breaker) avoids this.

Useful? React with 👍 / 👎.

@Jophkins Jophkins merged commit 7a6700b into main Feb 9, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant