Checkpoint: Application du skill itinova-user-management : authentification locale enrichie (login/email, 3 profils admin/standard/readonly), schéma DB étendu (firstName, lastName, login, isActive), page de connexion avec logo FEHAP, middleware writeProcedure pour les comptes readonly, formulaire Admin mis à jour. 33 tests Vitest passés, 0 erreur TypeScript.

This commit is contained in:
Manus
2026-04-21 06:45:32 -04:00
parent 11ffb77005
commit ff2a141ea4
15 changed files with 1391 additions and 219 deletions

View File

@@ -4,6 +4,7 @@ import { createServer } from "http";
import net from "net";
import { createExpressMiddleware } from "@trpc/server/adapters/express";
import { registerOAuthRoutes } from "./oauth";
import { registerStorageProxy } from "./storageProxy";
import { appRouter } from "../routers";
import { createContext } from "./context";
import { serveStatic, setupVite } from "./vite";
@@ -33,6 +34,8 @@ async function startServer() {
// Configure body parser with larger size limit for file uploads
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ limit: "50mb", extended: true }));
// Storage proxy for /manus-storage/* paths
registerStorageProxy(app);
// OAuth callback under /api/oauth/callback
registerOAuthRoutes(app);
// tRPC API

View File

@@ -0,0 +1,41 @@
import type { Express } from "express";
import { ENV } from "./env";
export function registerStorageProxy(app: Express) {
app.get("/manus-storage/*", async (req, res) => {
const key = (req.params as Record<string, string>)[0];
if (!key) {
res.status(400).send("Missing storage key");
return;
}
if (!ENV.forgeApiUrl || !ENV.forgeApiKey) {
res.status(500).send("Storage proxy not configured");
return;
}
try {
const forgeUrl = new URL(
"v1/storage/presign/get",
ENV.forgeApiUrl.replace(/\/+$/, "") + "/",
);
forgeUrl.searchParams.set("path", key);
const forgeResp = await fetch(forgeUrl, {
headers: { Authorization: `Bearer ${ENV.forgeApiKey}` },
});
if (!forgeResp.ok) {
const body = await forgeResp.text().catch(() => "");
console.error(`[StorageProxy] forge error: ${forgeResp.status} ${body}`);
res.status(502).send("Storage backend error");
return;
}
const { url } = (await forgeResp.json()) as { url: string };
if (!url) {
res.status(502).send("Empty signed URL from backend");
return;
}
res.set("Cache-Control", "no-store");
res.redirect(307, url);
} catch (err) {
console.error("[StorageProxy] failed:", err);
res.status(502).send("Storage proxy error");
}
});
}