NobGit
public anord read

Ledger

Why work hard when you can work easier?

Languages

Repository composition by tracked source files.

TypeScript
TypeScript 86% CSS 10% SQL 3% Shell 1% HTML 0%
Create file Wiki Documentation
Clone
https://nobgit.com/orgs/anord/ledger.git
ssh://[email protected]:2222/orgs/anord/ledger.git
attachments.ts
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import crypto from "node:crypto";
import { Router } from "express";
import { z } from "zod";
import { requireEditor } from "../middleware/auth.js";
import { env } from "../../config/env.js";
import { pool } from "../../db/pool.js";
import { getPageMetadata } from "../../services/pages.js";

const attachmentSchema = z.object({
  pageId: z.string().uuid(),
  fileName: z.string().min(1),
  contentType: z.string().min(3),
  base64Data: z.string().min(1)
});

export const attachmentsRouter = Router();

attachmentsRouter.get("/", async (req, res) => {
  const pageId = String(req.query.pageId ?? "");
  const page = await getPageMetadata(pageId, req.user ?? null);
  if (!page) {
    return res.status(404).json({ error: "Page not found" });
  }

  const result = await pool.query(
    `SELECT id, page_id, file_name, content_type, storage_path, size_bytes, created_at
     FROM attachments
     WHERE page_id = $1
     ORDER BY created_at DESC`,
    [pageId]
  );

  return res.json({ attachments: result.rows });
});

attachmentsRouter.post("/", requireEditor, async (req, res) => {
  const input = attachmentSchema.parse(req.body);
  const page = await getPageMetadata(input.pageId, req.user ?? null);
  if (!page) {
    return res.status(404).json({ error: "Page not found" });
  }

  if (env.STORAGE_PROVIDER !== "local") {
    return res.status(400).json({ error: "This Ledger deployment is configured for local storage only." });
  }

  const buffer = Buffer.from(input.base64Data, "base64");
  const fileId = crypto.randomUUID();
  const ext = path.extname(input.fileName);
  const fileName = `${fileId}${ext}`;

  await mkdir(env.LOCAL_STORAGE_ROOT, { recursive: true });
  const storagePath = path.join(env.LOCAL_STORAGE_ROOT, fileName);
  await writeFile(storagePath, buffer);

  const created = await pool.query(
    `
      INSERT INTO attachments (page_id, file_name, content_type, storage_path, size_bytes, created_by_user_id)
      VALUES ($1, $2, $3, $4, $5, $6)
      RETURNING id, file_name, storage_path, size_bytes
    `,
    [input.pageId, input.fileName, input.contentType, storagePath, buffer.length, req.user!.id]
  );

  return res.status(201).json(created.rows[0]);
});