A full-stack dashboard for academic project management and showcase publishing.
This repository now contains two systems that share authentication:
- Core Project Monitoring System (existing): project/task/milestone/review/file workflows
- Showcase System (new): versioned submission -> admin review -> publish pipeline
- Overview
- What Is New
- Tech Stack
- Architecture
- End-to-End Workflows
- Authentication and Access Control
- Core Project Monitoring Workflows
- CSV Assignment and Email Outbox Workflows
- Admin Project Control Workflows
- Showcase Module
- Database Models
- Routes
- Server Actions and APIs
- Notifications
- UI/UX Highlights
- Setup (Development)
- Deployment
- Environment Variables
- Operational Playbooks
- QA and Testing Matrix
- Seed Data
- Security Notes
- Troubleshooting
The app supports role-based dashboards:
- ADMIN: governance, users, full project control (mentor/member management), showcase review/publish
- TEACHER: project management + showcase authoring
- STUDENT: project participation + showcase authoring
- Backend-first authorization
- Strict role-based route guards
- Decoupled domain modules
- Type-safe backend with Prisma + Zod
- Switched to CoE portal SSO via shared JWT cookie (
coe_shared_token) across all protected routes - Middleware verifies the JWT with
COE_JWT_SECRETand injectsx-coe-email,x-coe-role, andx-coe-status - Users are auto-provisioned on first CoE login and pending assignments are resolved
- Removed NextAuth, OTP registration, password reset, and allowlist flows
- Added App Router error boundaries to replace generic production Server Components crash message
- route-level
error.tsx - app-level
global-error.tsx
- route-level
- Added public Major Projects explorer at
/majorprojects - Added public TE RBL Projects explorer at
/rblprojects-te - Updated navigation and middleware so both pages are publicly accessible
- Matched
/rblprojects-tevisual system to/majorprojectsfor consistent presentation
- Added Database Outbox Pattern for high-volume assignment notifications
- Admin CSV assignment processing now writes notification jobs into
EmailQueueinstead of sending synchronously - Added
PendingProjectAssignmentfor invite flows where user does not yet exist - Added secure background processor endpoint:
POST /api/cron/process-emails- token-protected via
EMAIL_QUEUE_CRON_SECRET
- Added admin monitoring page at
/admin/email-logswith retry for failed emails - Added manual queue trigger from admin UI (
Run Queue Now) for immediate processing - Added registration-time pending assignment sync:
- if a user registers after invite, pending rows for that email are auto-converted to
ProjectMember - pending rows are marked
ASSIGNED
- if a user registers after invite, pending rows for that email are auto-converted to
- Added
/admin/projectsto manage all projects centrally - Admin can:
- edit project metadata (title, description, domain, status, dates, max group size)
- change project mentor (teacher)
- add members to project
- change member roles (
MEMBER/LEAD) - remove members
- Server-side admin-only actions enforce access and validation for all project control operations
- Added complete decoupled showcase domain:
ShowcaseProjectProjectVersionReviewFeedbackProjectAssetShowcaseTeamMember
- Added strict status transition rules
- Added immutable version snapshots on submit/resubmit/edit events
- Added admin review and publish workflows
- Added user-facing multi-step structured submission workspace
- Added public showcase list + project detail pages (
/showcase,/showcase/[projectId])
New notification types:
PROJECT_SUBMITTEDFEEDBACK_ADDEDCHANGES_REQUESTEDPROJECT_APPROVEDPROJECT_PUBLISHED
- Added full bounce detection pipeline — automatically detects bounced invitation emails via Gmail API
- 6 single-responsibility detection modules:
- BounceFetcher — Gmail API client, searches for DSNs via
has:delivery-status, verifies MIME type, rate-limit backoff - BounceParser — Pure DSN extraction (recipient, diagnostic, Original-Message-ID) from RFC 3464/1894 bodies
- BounceValidator — Binary go/no-go: permanent (5xx) vs temporary (4xx), institutional domain check, SMTP code summarization
- BounceMatcher — Confidence-based correlation to
PendingProjectAssignment: HIGH (Message-ID match), MEDIUM (single email), LOW (multiple), NONE - BounceProcessor — Updates
PendingProjectAssignment.deliveryStatus = BOUNCEDwith diagnostic and reason - NotificationService — Creates in-app
PROJECT_UPDATEDnotification for the teacher, deduplicated by assignment identity
- BounceFetcher — Gmail API client, searches for DSNs via
- Message-ID from Gmail SMTP is now captured on
EmailQueuefor DSN correlation PendingProjectAssignmentstores bounce state:deliveryStatus,bounceDiagnosticRaw,bounceReason,lastBounceAt- Bounce detection runs via
POST /api/cron/detect-bounces(every 15 min recommended), protected byEMAIL_QUEUE_CRON_SECRET - New dependency:
googleapisfor Gmail API access withgmail.modifyscope - Teacher-facing bounce UI in MembersTab — ❌ icon + "Invitation delivery failed" + bounce reason
- Bounce cleared automatically when teacher edits the email address; resend preserves bounce state
- Gmail OAuth refresh token must be regenerated with
gmail.modifyscope
- Global command palette in topbar (quick navigation/actions)
- Keyboard shortcuts:
Ctrl/Cmd + Kopen command menuCtrl/Cmd + Bopen notifications
- Sidebar links for showcase management
- Enhanced dashboard visual surface/background polish
- Next.js 15 (App Router, standalone output)
- React 19 + TypeScript
- Prisma + MySQL
- CoE SSO (shared JWT cookie + middleware verification)
- TanStack Query + Zustand
- Tailwind CSS + shadcn/ui + Radix
- Framer Motion
- MinIO (S3-compatible uploads)
- Nodemailer (SMTP/Gmail)
- googleapis (Gmail API — DSN detection)
Shared Auth + Access Control
|
|---- Core Project Monitoring System
|
|---- Showcase System (Submission/Review/Publish)
| |
| -> Admin Control Surface (/admin/showcase)
|
|---- Automatic Invitation Delivery Tracking
|
-> Gmail API DSN Detection Pipeline
BounceFetcher → BounceParser → BounceValidator
→ BounceMatcher → BounceProcessor → NotificationService
Cron: POST /api/cron/detect-bounces
Shared:
- User identity/auth/session
- Role checks and middleware
- Notifications
- Email outbox queue + background processor
- Gmail OAuth credentials (SMTP + Gmail API DSN detection)
Decoupled:
- Core project domain models and logic
- Showcase domain models and logic
- Automatic Invitation Delivery Tracking (Gmail API pipeline + bounce detection modules)
This section captures full system behavior from onboarding to delivery and publishing.
- ADMIN:
- controls access policy and approvals
- governs users and all projects
- imports assignments and manages email outbox
- reviews and publishes showcase submissions
- TEACHER:
- creates and manages projects
- manages members, tasks, milestones, files, and reviews
- authors showcase submissions
- STUDENT:
- joins assigned projects
- executes tasks and milestone work
- receives notifications
- contributes to showcase submissions
- Auth lifecycle:
- CoE login -> JWT verified -> user resolved -> role access checks
- Assignment lifecycle:
- CSV row -> project and user resolution -> member or pending invite -> queued email
- Outbox lifecycle:
- PENDING -> PROCESSING -> SENT or FAILED
- Bounce detection lifecycle:
- DSN received in Gmail inbox -> BounceFetcher fetches -> BounceParser extracts -> BounceValidator validates
- -> BounceMatcher correlates (HIGH/MEDIUM confidence) -> BounceProcessor updates deliveryStatus -> NotificationService alerts teacher
- Core project lifecycle:
- creation -> active execution -> review and progress tracking -> completion
- Showcase lifecycle:
- DRAFT -> SUBMITTED -> UNDER_REVIEW -> CHANGES_REQUESTED or APPROVED/REJECTED -> PUBLISHED
- Users authenticate via the CoE portal, which sets a shared JWT cookie (
coe_shared_token) - Middleware verifies the JWT with
COE_JWT_SECRET, maps roles (ADMIN/FACULTY/STUDENT), and injectsx-coe-email,x-coe-role,x-coe-status - Server guards (
requireCoeUser,requireRole) resolve or auto-provision users and enforce role access - Non-ACTIVE status is rejected at guard time
Public:
/showcase/showcase/[projectId]/majorprojects/rblprojects-te
Protected:
/admin/*(ADMIN)/teacher/*(TEACHER)/student/*(STUDENT)/showcase/my-projects(TEACHER/STUDENT)
- Middleware enforces route-level access.
- Server actions enforce operation-level access.
- Every privileged operation validates role server-side.
- UI visibility is not treated as a security boundary.
- Teacher creates a project with timeline, domain, and group size constraints.
- Optional tags are attached for discoverability.
- Project appears in teacher project list and dashboard counters.
- Members are added by student ID, email, or roll number.
- Max group size is enforced before insertion.
- Roles are maintained as MEMBER or LEAD.
- Member removal deletes project-member linkage.
- Tasks are created and assigned to users.
- Milestones define target checkpoints.
- Completion percentages are recalculated and reflected in analytics components.
- Reviews are scheduled and tracked per project.
- Files and comments provide project evidence and collaboration context.
- Notification events keep members synchronized.
- Required headers:
- projectName
- Parser behavior:
- handles quoted CSV values
- normalizes email casing and whitespace
- skips invalid rows
- deduplicates repeated email + projectName pairs
- Resolve project by projectName.
- Auto-create project when title is not found.
- Resolve users by email.
- Existing user rows create ProjectMember entries.
- Missing users create PendingProjectAssignment entries.
- All valid rows create EmailQueue jobs with PENDING status.
- Invited users sign in via the CoE portal when ready.
- First CoE login auto-provisions the account and checks pending assignments by email.
- Matching entries are converted into ProjectMember rows.
- Pending entries are marked ASSIGNED.
- Projects become visible immediately in student project list.
- Queue processing flow:
- claim oldest PENDING rows as PROCESSING
- send with controlled stagger delay
- mark SENT on success with Gmail
messageIdcaptured for DSN correlation - requeue or mark FAILED based on retry count
- Admin controls:
- Retry Failed
- Run Queue Now
- secured cron endpoint at /api/cron/process-emails
- Bounce detection:
POST /api/cron/detect-bouncesqueries Gmail for unread DSNs and correlates them toPendingProjectAssignmentrecords- Matched bounces update
deliveryStatus = BOUNCEDand notify the teacher in-app
Route: /admin/projects
- Admin can view all projects across mentors.
- Search supports title, domain, and mentor matching.
- Admin can edit title, description, domain, status, dates, and max group size.
- Validation runs in admin server actions.
- Relevant pages are revalidated after update.
- Admin selects active teacher as mentor.
- Backend validates teacher role and active state.
- Project mentor updates propagate to downstream teacher views.
- Add member with MEMBER or LEAD role.
- Update existing member role.
- Remove member from project.
- Capacity and consistency checks are applied server-side.
DRAFT -> SUBMITTED -> UNDER_REVIEW -> (CHANGES_REQUESTED | APPROVED | REJECTED)
CHANGES_REQUESTED -> SUBMITTED -> UNDER_REVIEW
APPROVED -> PUBLISHED
- Basic information: title, short description, full description
- Project details: problem statement, objectives, methodology, key features
- Technical details: tech stack, architecture, database, API integrations
- Resources: GitHub, live demo, documentation link/files, screenshots
- Team information: members + mentor
- Additional: categories/tags, project domain, visibility
- Never overwrite past submission state
- Every submit/resubmit creates a new
ProjectVersion - Admin actions enforce valid status transitions
- Public page shows only
status = PUBLISHED && isPublic = true
titleis requiredshortDescriptionis required- At least 2 major content sections must be filled before submission
- GitHub URL OR documentation (link or file reference) is required before submission
- View submissions at
/admin/showcase - Filter by status
- Start review
- Add/resolve feedback
- Request changes / approve / publish / reject
- Create/edit project at
/showcase/my-projects - Submit when in
DRAFT - Resubmit when in
CHANGES_REQUESTED - View latest feedback and version context
- Draft authoring:
- creator builds structured content, assets, and links
- Submit event:
- backend validates required fields and minimum section coverage
- immutable ProjectVersion snapshot is created
- status transitions to SUBMITTED
- Review event:
- admin starts review and adds feedback entries
- admin can request changes, approve, reject, or publish
- Resubmission event:
- creator updates content and resubmits
- new snapshot is created; previous snapshots remain immutable
- Publication event:
- project appears publicly only when status is PUBLISHED and visibility is enabled
- Each submission cycle produces an immutable version record.
- Feedback remains tied to review context, preserving traceability.
- Current editable state never overwrites historical submission evidence.
User,Project,ProjectMember,Task,Milestone,Review,ReviewCriteria,ProjectFile,Comment,Notification,Tag,ProjectTag
EmailQueuePendingProjectAssignmentShowcaseProjectProjectVersionReviewFeedbackProjectAssetShowcaseTeamMember
ShowcaseProjectStatusShowcaseProjectDomainShowcaseAssetKindEmailQueueStatusDeliveryStatus(BOUNCED) — tracks invitation delivery failures- Added notification enum values for showcase events
EmailQueue.messageId— Gmail Message-ID captured on send, used as primary DSN correlation keyPendingProjectAssignment.deliveryStatus—null(no issue) orBOUNCED(delivery failed)PendingProjectAssignment.bounceDiagnosticRaw— raw SMTP diagnostic from DSNPendingProjectAssignment.bounceReason— human-readable bounce summary for teacher UIPendingProjectAssignment.lastBounceAt— timestamp of when DSN was detected
/showcase/showcase/[projectId]/majorprojects/rblprojects-te
/admin/admin/projects/admin/users/admin/teacher-approvals/admin/project-assignments/admin/email-logs/admin/showcase/admin/showcase/[projectId]/admin/settings
/teacher/teacher/projects/teacher/projects/new/teacher/projects/[projectId]/teacher/analytics
/student/student/projects/student/projects/[projectId]/student/notifications
/showcase/my-projects
getPendingTeacherRegistrations()approveTeacherRegistration()rejectTeacherRegistration()
adminUploadProjectAssignments()getAdminAssignableProjects()getEmailQueueLogs()retryFailedEmails()runEmailQueueNow()
getAdminProjectsManagementData()adminUpdateProject()adminUpdateProjectMentor()adminAddProjectMember()adminUpdateProjectMemberRole()adminRemoveProjectMember()
processEmailQueue(batchSize = 50)- claims
PENDINGrows asPROCESSING - sends via pooled Nodemailer transporter
- marks
SENTor requeues/fails withattemptsanderrorLog - stores Gmail
messageIdonEmailQueueon successful send
- claims
detectBounces()— orchestrates the full bounce detection pipeline:BounceFetcher.fetchNew()— queries Gmail API for unread DSNs (has:delivery-status is:unread)BounceParser.parse()— extracts recipient, diagnostic, Original-Message-ID from DSN bodyBounceValidator.validate()— checks permanent failure, institutional domain, required fieldsBounceMatcher.match()— correlates DSN toPendingProjectAssignmentwith confidence (HIGH/MEDIUM/LOW/NONE)BounceProcessor.process()— updatesdeliveryStatus = BOUNCEDwith diagnostic and reasonNotificationService.notifyBounce()— creates in-app notification for the teacher
- Exposed via
POST /api/cron/detect-bounces(protected byEMAIL_QUEUE_CRON_SECRET)
User-side:
createProject()updateProject()submitProject()resubmitProject()getMyProjects()getProjectVersions()
Admin-side:
getAllSubmissions()getSubmissionById()startReview()addFeedback()resolveFeedback()requestChanges()approveProject()publishProject()rejectProject()
Public:
getPublicShowcaseProjects()getPublicShowcaseProjectById()
POST /api/cron/process-emails— process pending email queuePOST /api/cron/detect-bounces— run bounce detection pipeline (Gmail API DSN search → parse → validate → match → process → notify)
Existing project notifications remain unchanged.
Showcase events now emit notifications for:
- submission
- feedback added
- changes requested
- approved
- published
Invitation bounce detection emits a PROJECT_UPDATED notification when a bounce is confirmed:
- Recipient: project teacher
- Title: "Invitation delivery failed"
- Message: includes the recipient email and bounce reason (e.g., "Mailbox does not exist")
- Deduplication: at most one notification per
PendingProjectAssignmentrecord lifetime - Edit clears bounce (new record → eligible for fresh notification); resend preserves bounce
- Role-aware sidebar nav links
- Topbar command menu with quick actions
- Improved shell visuals with subtle gradients and texture
- CoE portal handles login; the app relies on the shared JWT cookie
- Unauthenticated access redirects to the CoE login entry point
- Teacher approvals panel for admin activation workflows
- Email logs panel for outbox status + retry failed
- Projects management panel for editing project details, mentor, and members
- Showcase command center and structured review view
- Bounce state indicators in MembersTab pending cards — teachers see ❌ with bounce reason when delivery fails; edit to clear, resend preserves
- Pending invitation cards show bounce state in MembersTab:
- Normal: ⏳ clock icon + "Invitation sent · 2 days ago"
- Bounced: ❌ red icon + "Invitation delivery failed" + bounce reason (e.g., "Mailbox does not exist")
- Edit clears bounce; resend preserves bounce state
- Teachers are auto-notified via in-app notification when a bounce is detected
- Admin CSV assignment import requires only
emailandprojectName - Projects are auto-resolved by project title from each CSV row (no manual project picker)
- CSV assignment import supports both existing users and new invitees
- Existing users are assigned directly to
ProjectMember - Non-existing users are stored as
PendingProjectAssignment - When invited users sign in via CoE, pending assignments are automatically linked and moved into
ProjectMember - All assignment notifications are queued and processed asynchronously
- Multi-step project submission form (stepper)
- Structured section cards for review readability
- Basic version comparison in admin review
- Public project detail pages with sectioned narrative + screenshot gallery
- Unified neutral visual language across
/majorprojectsand/rblprojects-te - Scroll-aware but width-safe table containers to prevent overflow beyond control-bar width
- Responsive search + class/group filters with consistent interaction states
npm cicp .env.example .envnpm run db:generate
npm run db:pushOptional seed:
npm run db:seednpm run devFor complete production steps, see:
Includes:
- Docker deployment
- PM2 deployment
- SSL/nginx setup
- migration strategy
- troubleshooting and backups
DATABASE_URL="mysql://user:password@host:3306/project_dashboard"
COE_JWT_SECRET="<coe-jwt-secret>"
EMAIL_QUEUE_CRON_SECRET="<strong-random-secret>"
INSTITUTIONAL_EMAIL_DOMAIN="tcetmumbai.in"INSTITUTIONAL_EMAIL_DOMAIN="tcetmumbai.in"INSTITUTIONAL_EMAIL_DOMAIN controls which email domains are accepted for student invitations and bounce detection validation. Defaults to "tcetmumbai.in" if not set.
S3_BUCKET_NAME="..."
MINIO_ENDPOINT="https://minio.your-domain.com"
MINIO_REGION="us-east-1"
MINIO_ACCESS_KEY="..."
MINIO_SECRET_KEY="..."
S3_FORCE_PATH_STYLE="true"
MINIO_USE_PROXY="false"MINIO_USE_PROXY="true" forces app-served URLs (/api/storage/...) instead of direct MinIO presigned URLs.
SMTP_PROVIDER="gmail"
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_SECURE="false"
SMTP_USER="your-email@gmail.com"
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
GOOGLE_REFRESH_TOKEN="your-google-refresh-token"
SMTP_FROM="your-email@gmail.com"Note: SMTP is required for outbound email queue notifications.
Note: bulk email processing route requires EMAIL_QUEUE_CRON_SECRET.
Gmail API (required for bounce detection):
The GOOGLE_REFRESH_TOKEN must be generated with both scopes:
https://mail.google.com/— existing SMTP scopehttps://www.googleapis.com/auth/gmail.modify— Gmail API DSN detection (read + remove UNREAD label)
After adding gmail.modify to the OAuth consent screen, regenerate the refresh token. The old token lacks the required scope and will cause 401 errors.
- Confirm
COE_JWT_SECRETis configured and CoE portal issuescoe_shared_token. - Import assignment CSV with email and projectName.
- Review import summary:
- matched rows
- created projects
- direct assignments
- pending invites
- Run Queue Now for immediate notification delivery.
- Confirm invited users convert to memberships after first CoE login.
- Open /admin/projects.
- Update project mentor to active teacher.
- Validate teacher access on teacher project screens.
- Inspect FAILED rows in /admin/email-logs.
- Fix SMTP or provider credentials/connectivity.
- Click Retry Failed.
- Click Run Queue Now.
- Verify rows move to SENT or requeue with updated error logs.
- Configure cron schedule for
POST /api/cron/detect-bounces(every 15 min recommended) - Regenerate Gmail OAuth refresh token with
gmail.modifyscope if bounce detection returns 401 - Check
PendingProjectAssignmentrecords withdeliveryStatus = BOUNCEDto review bounce reasons - Teachers receive in-app notification on bounce; instruct them to edit the email address to clear bounce state
- Monitor for repeated LOW confidence matches (same email, multiple projects) — may indicate CSV assignment collisions
- Open /admin/showcase.
- Filter by submission state.
- Start review and process feedback.
- Request changes or approve.
- Publish approved items.
- Verify public visibility in /showcase.
- CoE JWT verification failures and missing cookie handling.
- Role mapping for ADMIN/FACULTY/STUDENT and non-ACTIVE status rejection.
- Route guard checks across all role scopes.
- CSV parsing with quoted values and malformed lines.
- Existing user direct assignment path.
- New user pending assignment path.
- Registration-time pending assignment conversion.
- Queue retry and failure threshold logic.
- Metadata edits with valid and invalid payloads.
- Mentor reassignment to inactive or wrong-role users should fail.
- Member add operations enforcing max group size.
- Member role transitions including LEAD updates.
- Member removal consistency across project views.
- DSN parser extracts recipient, diagnostic, and Message-ID from RFC 3464/1894 bodies.
- Validator rejects temporary failures (4xx), missing recipients, and non-institutional domains.
- Matcher returns correct confidence: HIGH (Message-ID match), MEDIUM (single email), LOW (multiple), NONE.
- Full pipeline produces
deliveryStatus = BOUNCEDon permanent failures. - Duplicate DSNs are idempotent — second run produces no state change.
- Edit clears bounce; resend preserves bounce.
- Notification is created only once per assignment record.
- Submission validation constraints.
- Status transition integrity.
- Snapshot creation on submit and resubmit.
- Public listing restricted to published and visible submissions.
Seed creates:
- default admin (
admin@university.edu) - teacher and student users
- sample projects, tasks, milestones, reviews, notifications
Seeded users are created with placeholder password hashes (not used for CoE auth).
- Keep
.envout of git - Rotate AWS and SMTP credentials regularly
- Use a strong
COE_JWT_SECRETand rotate it with the CoE portal - Prefer
prisma migrate deployin production - Restrict DB exposure to private network
- Use TLS/HTTPS in production
- Non-ACTIVE CoE statuses are blocked by server guards
- Protect
/api/cron/process-emailsand/api/cron/detect-bounceswith a strong secret and never expose them client-side - Gmail OAuth refresh token must include
gmail.modifyscope for bounce detection; regenerate after scope changes
- Verify the invited user has completed CoE login at least once.
- Verify pending assignment email matches registered email (normalized lowercase).
- Verify PendingProjectAssignment status changes to ASSIGNED.
- Verify ProjectMember entries were created for the new user.
- Verify SMTP environment variables.
- Use Run Queue Now in /admin/email-logs.
- Verify cron token and scheduler invocation for /api/cron/process-emails.
- The Gmail OAuth refresh token was issued without
gmail.modifyscope. - Regenerate the refresh token with both
https://mail.google.com/(SMTP) andhttps://www.googleapis.com/auth/gmail.modify(DSN detection) scopes. - Update
GOOGLE_REFRESH_TOKENin.env.
- Verify Gmail API is enabled in GCP Console.
- Verify
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET, andGOOGLE_REFRESH_TOKENare set. - Verify the sending mailbox actually receives DSNs (check Gmail inbox manually).
- Check that
is:unreadis not filtering already-read DSNs.
- The
PROJECT_UPDATEDnotification type must be present in theNotificationTypeenum. - Verify cron ran successfully — check
POST /api/cron/detect-bouncesresponse. - The notification is deduplicated per assignment record — only the first detection creates one.
- Confirm selected mentor is active TEACHER.
- Confirm action is performed by ADMIN session.
- If
prisma migrate devreports drift and asks for reset on a real database, do not reset. - Baseline the current schema into migrations:
mkdir -p prisma/migrations/0001_baselinenpx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0001_baseline/migration.sqlnpx prisma migrate resolve --applied 0001_baseline
- After baselining, future schema changes can use normal
prisma migrate devworkflows.
- In standalone output mode, run with node .next/standalone/server.js in production instead of next start.
- If
MINIO_ENDPOINTpoints tolocalhostor a private host, browsers cannot fetch presigned URLs directly. - Set
MINIO_USE_PROXY="true"to stream assets through/api/storage/[...path]from the app domain.
- File and showcase asset downloads are served through internal proxy routes (
/api/storage/[...path]). - Direct MinIO presigned download URLs are not returned to the browser.
- This keeps MinIO endpoints internal while Node streams file content to clients.