tercul-frontend/server/index.ts
Damir Mukimov 4a23f496fa
Major frontend development updates
- Enhanced annotation system with improved inline editing
- Updated author components with new card and header designs
- Improved reading view with enhanced line numbering and controls
- Added new blog management features and tag management
- Updated UI components with improved accessibility and styling
- Enhanced search functionality with better filtering
- Added new dashboard features and activity feeds
- Improved translation selector and work comparison tools
- Updated GraphQL integration and API hooks
- Enhanced responsive design and mobile experience
2025-11-27 03:44:09 +01:00

105 lines
3.0 KiB
TypeScript

import express, {
type NextFunction,
type Request,
type Response,
} from "express";
import { attachGraphQLClient } from "./middleware/graphql";
import commentRouter from "./routes/comment";
import userRouter from "./routes/user";
import authorRouter from "./routes/author";
import workRouter from "./routes/work";
import tagRouter from "./routes/tag";
import collectionRouter from "./routes/collection";
import blogRouter from "./routes/blog";
import statsRouter from "./routes/stats";
import { log, serveStatic, setupVite } from "./vite";
import { createServer } from "node:http";
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// Attach per-request GraphQL client
app.use(attachGraphQLClient);
app.use((req, res, next) => {
const start = Date.now();
const path = req.path;
let capturedJsonResponse: unknown;
const originalResJson = res.json;
res.json = (bodyJson, ...args) => {
capturedJsonResponse = bodyJson;
return originalResJson.apply(res, [bodyJson, ...args]);
};
res.on("finish", () => {
const duration = Date.now() - start;
if (path.startsWith("/api")) {
let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
if (capturedJsonResponse) {
logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
}
if (logLine.length > 80) {
logLine = `${logLine.slice(0, 79)}`;
}
log(logLine);
}
});
next();
});
(async () => {
// Domain routers (incremental migration from monolith)
app.use("/api/users", userRouter);
app.use("/api/authors", authorRouter);
app.use("/api/works", workRouter);
app.use("/api/tags", tagRouter);
app.use("/api/comments", commentRouter);
app.use("/api/collections", collectionRouter);
app.use("/api/blog", blogRouter);
app.use("/api/stats", statsRouter);
// comments router already mounted earlier at /api/comments
const server = createServer(app);
app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => {
const fallbackStatus = 500;
interface StatusCarrier {
status?: number;
statusCode?: number;
}
const status =
typeof err === "object" && err
? ((err as StatusCarrier).status ??
(err as StatusCarrier).statusCode ??
fallbackStatus)
: fallbackStatus;
const message =
err instanceof Error ? err.message : "Internal Server Error";
res.status(status).json({ message });
throw err;
});
// importantly only setup vite in development and after
// setting up all the other routes so the catch-all route
// doesn't interfere with the other routes
if (app.get("env") === "development") {
await setupVite(app, server);
} else {
serveStatic(app);
}
// ALWAYS serve the app on port 5000
// this serves both the API and the client.
// It is the only port that is not firewalled.
const port = 5000;
server.listen(port, "0.0.0.0", () => {
log(`serving on port ${port}`);
});
})();