Files
home/Projects/kompose/news/apps/backend/src/app.ts
2025-10-08 10:35:48 +02:00

166 lines
3.9 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"))
})