feat(backend): scaffold Express API with health and module layout

Add src/routes/notifications, services/pushService, models/device,
scheduler stubs, and entrypoint with GET /health and startup log.
This commit is contained in:
Jose Olarte III
2026-05-11 14:33:32 +08:00
commit 94c38bac74
9 changed files with 1593 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
dist/
.env
*.log

1493
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "notification-wakeup-service",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"start": "tsx src/index.ts",
"build": "tsc"
},
"dependencies": {
"express": "^5.1.0"
},
"devDependencies": {
"@types/express": "^5.0.0",
"@types/node": "^22.10.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
}

20
src/index.ts Normal file
View File

@@ -0,0 +1,20 @@
import express from "express";
import { notificationsRouter } from "./routes/notifications.js";
import { startScheduler } from "./scheduler.js";
const app = express();
const port = Number(process.env.PORT) || 3000;
app.use(express.json());
app.get("/health", (_req, res) => {
res.status(200).json({ status: "ok" });
});
app.use("/notifications", notificationsRouter);
startScheduler();
app.listen(port, () => {
console.log("* Running backend");
});

7
src/models/device.ts Normal file
View File

@@ -0,0 +1,7 @@
export interface Device {
id: string;
pushToken: string;
platform: "ios" | "android" | "web";
createdAt: Date;
updatedAt: Date;
}

View File

@@ -0,0 +1,7 @@
import { Router } from "express";
export const notificationsRouter = Router();
notificationsRouter.get("/", (_req, res) => {
res.json({ ok: true, resource: "notifications" });
});

16
src/scheduler.ts Normal file
View File

@@ -0,0 +1,16 @@
let intervalId: ReturnType<typeof setInterval> | undefined;
export function startScheduler(): void {
if (intervalId !== undefined) return;
// TODO: replace with job queue or cron for wake-up checks
intervalId = setInterval(() => {
// placeholder tick
}, 60_000);
}
export function stopScheduler(): void {
if (intervalId !== undefined) {
clearInterval(intervalId);
intervalId = undefined;
}
}

View File

@@ -0,0 +1,8 @@
import type { Device } from "../models/device.js";
export async function sendPushToDevice(
_device: Device,
_payload: Record<string, unknown>
): Promise<void> {
// TODO: integrate with push provider (FCM, APNs, etc.)
}

18
tsconfig.json Normal file
View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}