Skip to content

Updated NonProfit Files#22

Open
TrickkyRicky wants to merge 1 commit intomainfrom
feat-file-system-integration-flow
Open

Updated NonProfit Files#22
TrickkyRicky wants to merge 1 commit intomainfrom
feat-file-system-integration-flow

Conversation

@TrickkyRicky
Copy link
Contributor

What changed

NonprofitDocument was storing raw file bytes in a fileData Bytes column. So we now store the reference to where the file is located with the new filePath column. Existing records are left the same and that flow is fine but new files going forward will have the filePath column populated instead of fileData.


Two storage paths

Record type fileData filePath Download behaviour
Legacy has bytes NULL Served from DB blob
New uploads NULL has path Served from disk

The download endpoint handles both transparently


Key changes

Database

  • fileData made optional (Bytes?) existing rows left same
  • filePath String? added NULL on all existing rows

API

  • GET /api/nonprofit-documents list endpoint, returns metadata only
  • GET /api/nonprofit-documents/download?id= new dedicated download endpoint; checks filePath first, falls back to fileData for legacy records
  • POST /api/nonprofit-documents writes to disk, stores filePath, clears fileData on update
  • PATCH /api/nonprofit-documents same as POST; also deletes the old file from disk when replacing

Other

  • docker-compose.ymluploads/ directory mounted as a named volume so files survive container restarts
  • Admin UI — download buttons now call the dedicated endpoint instead of base64-decoding blobs client-side

@TrickkyRicky TrickkyRicky requested a review from a team as a code owner March 1, 2026 04:20
@TrickkyRicky TrickkyRicky linked an issue Mar 1, 2026 that may be closed by this pull request
4 tasks
Copy link

@kierstinhicks kierstinhicks left a comment

Choose a reason for hiding this comment

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

Looks good. Approved.

import { prisma } from '@/lib/prisma';

export async function GET(request: Request) {
const UPLOAD_DIR = path.join(process.cwd(), 'uploads', 'nonprofit-documents');
Copy link
Contributor

@mrysav mrysav Mar 2, 2026

Choose a reason for hiding this comment

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

Can you make this configurable via environment variable? eg FILE_UPLOADS or something?

When this is deployed via our Coolify/Docker set up, I will create a Docker volume and mount it at some arbitrary point, and set the uploads directory to that.

edit: Okay, I do see you're effectively doing that with the new volume, but IMO it would be better to make it explicitly configurable rather than rely on process.cwd


// Write new file to disk
await mkdir(UPLOAD_DIR, { recursive: true });
const diskFileName = `${Date.now()}-${file.name}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

non-blocker: rather than Date.now(), can you either

  • use a custom date format so there are eg. no spaces or special characters, eg. 20260301230200, or
  • use a plain epoch timestamp?


// Write new file to disk
await mkdir(UPLOAD_DIR, { recursive: true });
const diskFileName = `${Date.now()}-${file.name}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

See above

expect(data).toEqual({ error: 'User not found' });
});

it('should allow a roleless user to set their own role during onboarding', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if there is a test for this already, but I don't think we should allow users to onboard themselves as Admin

Comment on lines +152 to +163
let filePath: string | undefined;

if (documentData?.fileData) {
const fileBuffer = Buffer.from(
Object.values(documentData.fileData) as number[]
);
await mkdir(UPLOAD_DIR, { recursive: true });
const diskFileName = `${documentId}-${documentData.fileName || 'document'}`;
filePath = path.join(UPLOAD_DIR, diskFileName);
await writeFile(filePath, fileBuffer);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Can functions like these be separated out into their own helper class? I feel like I'm seeing some file-related features duplicated (eg. getting a file name) and we don't want a situation where refactoring means changing a bunch of places.

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.

Store Nonprofit Documents on the File System Instead of the Database

3 participants