Files
home/Projects/kompose/news/apps/backend/src/app.ts
2025-10-10 16:43:21 +02:00

166 lines
3.7 KiB
TypeScript

import * as trpcExpress from "@trpc/server/adapters/express";
import path from "path";
import express from "express";
import cors from "cors";
import { prisma } from "./utils/prisma";
import swaggerUi from "swagger-ui-express";
import { createContext, router } from "./trpc";
import { userRouter } from "./user/router";
import { listRouter } from "./list/router";
import { organizationRouter } from "./organization/router";
import { subscriberRouter } from "./subscriber/router";
import { templateRouter } from "./template/router";
import { campaignRouter } from "./campaign/router";
import { messageRouter } from "./message/router";
import { settingsRouter } from "./settings/router";
import swaggerSpec from "./swagger";
import { apiRouter } from "./api/server";
import { dashboardRouter } from "./dashboard/router";
import { statsRouter } from "./stats/router";
import { ONE_PX_PNG } from "./constants";
const appRouter = router({
user: userRouter,
list: listRouter,
organization: organizationRouter,
subscriber: subscriberRouter,
template: templateRouter,
campaign: campaignRouter,
message: messageRouter,
settings: settingsRouter,
dashboard: dashboardRouter,
stats: statsRouter,
});
export type AppRouter = typeof appRouter;
export const app = express();
app.use(
cors({
origin: ["http://localhost:3000", "http://localhost:4173"],
}),
);
app.use(express.json());
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.get("/t/:id", async (req, res) => {
try {
const { id } = req.params;
const subscriberId = req.query.sid;
const trackedLink = await prisma.trackedLink.findUnique({
where: { id },
});
if (!trackedLink) {
res.status(404).send("Link not found");
return;
}
res.redirect(trackedLink.url);
if (subscriberId && typeof subscriberId === "string") {
await prisma
.$transaction(async (tx) => {
// add a new click
await tx.click.create({
data: {
subscriberId,
trackedLinkId: trackedLink.id,
},
});
if (!trackedLink.campaignId) return;
const message = await tx.message.findFirst({
where: {
campaignId: trackedLink.campaignId,
subscriberId,
status: {
not: "CLICKED",
},
},
});
if (!message) return;
await tx.message.update({
where: {
id: message.id,
},
data: {
status: "CLICKED",
},
});
})
.catch((error) => {
console.error("Error updating message status", error);
});
}
} catch (error) {
res.status(404).send("Link not found");
}
});
app.get("/img/:id/img.png", async (req, res) => {
// Send pixel immediately
const pixel = Buffer.from(ONE_PX_PNG, "base64");
res.setHeader("Content-Type", "image/png");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
res.end(pixel);
const id = req.params.id;
try {
await prisma.$transaction(async (tx) => {
const message = await tx.message.findUnique({
where: {
id,
Campaign: {
openTracking: true,
},
},
});
if (!message) {
return;
}
if (message.status !== "SENT") return;
await tx.message.update({
where: { id },
data: {
status: "OPENED",
},
});
});
} catch (error) {
console.error("Error updating message status", error);
}
});
app.use("/api", apiRouter);
app.use(
"/trpc",
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
}),
);
const staticPath = path.join(__dirname, "..", "..", "web", "dist");
// serve SPA content
app.use(express.static(staticPath));
app.get("*", (_, res) => {
res.sendFile(path.join(staticPath, "index.html"));
});