More udpates to documentation generation

This commit is contained in:
2026-02-17 10:36:41 -07:00
parent d3287a0fa4
commit 58dc1942ec
840 changed files with 1228847 additions and 95 deletions

View File

@@ -1,4 +1,4 @@
import { readdir, readFile, writeFile, mkdir, rm, rename, stat } from 'fs/promises';
import { readdir, readFile, writeFile, mkdir, rm, rename, stat, copyFile } from 'fs/promises';
import { resolve as pathResolve, join, normalize, dirname, extname } from 'path';
import crypto from 'crypto';
import { env } from '../../config/env';
@@ -250,6 +250,29 @@ function isEditableFile(relativePath: string): boolean {
return ['.md', '.txt', '.yml', '.yaml', '.json', '.css', '.html', '.js'].includes(ext);
}
const ALLOWED_UPLOAD_EXTENSIONS = new Set([
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico',
'.pdf', '.zip',
]);
async function uploadFile(relativePath: string, sourcePath: string): Promise<void> {
const ext = extname(relativePath).toLowerCase();
if (!ALLOWED_UPLOAD_EXTENSIONS.has(ext)) {
throw new Error(`File type not allowed: ${ext}`);
}
const fullPath = safeResolve(relativePath);
await mkdir(dirname(fullPath), { recursive: true });
await copyFile(sourcePath, fullPath);
// Invalidate tree cache (structure changed)
try {
await redis.del(TREE_CACHE_KEY);
} catch (err) {
logger.warn('Failed to invalidate tree cache after upload:', err);
}
}
async function invalidateTreeCache(): Promise<void> {
try {
await redis.del(TREE_CACHE_KEY);
@@ -265,6 +288,7 @@ export const docsFilesService = {
createFile,
deleteFile,
renameFile,
uploadFile,
safeResolve,
isEditableFile,
invalidateTreeCache,

View File

@@ -1,4 +1,7 @@
import { Router, Request, Response, NextFunction } from 'express';
import multer from 'multer';
import { rm } from 'fs/promises';
import { extname } from 'path';
import { authenticate } from '../../middleware/auth.middleware';
import { requireNonTemp, requireRole } from '../../middleware/rbac.middleware';
import { env } from '../../config/env';
@@ -104,6 +107,57 @@ router.post(
},
);
// --- File Upload ---
const ALLOWED_UPLOAD_EXTENSIONS = new Set([
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico',
'.pdf', '.zip',
]);
const upload = multer({
storage: multer.diskStorage({}), // temp dir
limits: { fileSize: 20 * 1024 * 1024 }, // 20MB
fileFilter: (_req, file, cb) => {
const ext = extname(file.originalname).toLowerCase();
if (ALLOWED_UPLOAD_EXTENSIONS.has(ext)) {
cb(null, true);
} else {
cb(new Error(`File type not allowed: ${ext}`));
}
},
});
// POST /api/docs/upload — upload binary file (image, pdf, etc.)
router.post(
'/upload',
upload.single('file'),
async (req: Request, res: Response, next: NextFunction) => {
const tempPath = req.file?.path;
try {
cm_docs_operations.inc({ operation: 'upload' });
if (!req.file) {
res.status(400).json({ error: { message: 'No file provided', code: 'VALIDATION_ERROR' } });
return;
}
const targetDir = (req.body as { path?: string }).path || '';
const fileName = req.file.originalname;
const relativePath = targetDir ? `${targetDir}/${fileName}` : fileName;
await docsFilesService.uploadFile(relativePath, req.file.path);
// Clean up temp file
try { await rm(req.file.path); } catch { /* ignore */ }
res.json({ success: true, path: relativePath });
} catch (err) {
// Clean up temp file on error
if (tempPath) { try { await rm(tempPath); } catch { /* ignore */ } }
handleFileError(err, res, next);
}
},
);
// --- File Management Endpoints ---
// GET /api/docs/files — list file tree