feat: add sequelize with postgres UTC timestamp endpoint

This commit is contained in:
Noah Holt 2026-08-11 00:15:40 -05:00
parent 1365bd7644
commit 8237c6cd2a
8 changed files with 450 additions and 7 deletions

26
src/config/database.ts Normal file
View file

@ -0,0 +1,26 @@
import { Sequelize } from "sequelize";
const dbUrl = process.env.DATABASE_URL;
const dbHost = process.env.DB_HOST;
const dbPort = Number(process.env.DB_PORT ?? 5432);
const dbName = process.env.DB_NAME;
const dbUser = process.env.DB_USER;
const dbPassword = process.env.DB_PASSWORD;
let sequelize: Sequelize | null = null;
if (dbUrl) {
sequelize = new Sequelize(dbUrl, {
dialect: "postgres",
logging: process.env.DB_LOGGING === "true" ? console.log : false,
});
} else if (dbHost && dbName && dbUser) {
sequelize = new Sequelize(dbName, dbUser, dbPassword, {
host: dbHost,
port: dbPort,
dialect: "postgres",
logging: process.env.DB_LOGGING === "true" ? console.log : false,
});
}
export { sequelize };

View file

@ -1,7 +1,14 @@
import { describe, it, expect } from "vitest";
import { describe, it, expect, vi, beforeEach } from "vitest";
import request from "supertest";
import { app } from "./index.js";
import { greeting } from "./greeting.js";
import * as db from "./services/database.js";
const getUtcTimestampSpy = vi.spyOn(db, "getUtcTimestamp");
beforeEach(() => {
getUtcTimestampSpy.mockReset();
});
describe("greeting", () => {
it("mentions the homelab", () => {
@ -31,3 +38,30 @@ describe("GET /", () => {
});
});
});
describe("GET /utc-time", () => {
it("returns 503 when database is not configured", async () => {
getUtcTimestampSpy.mockResolvedValueOnce(null);
const res = await request(app).get("/utc-time");
expect(res.status).toBe(503);
expect(res.body).toHaveProperty("error");
});
it("returns UTC timestamp when database is available", async () => {
const mockTime = "2026-01-01T00:00:00.000Z";
getUtcTimestampSpy.mockResolvedValueOnce(mockTime);
const res = await request(app).get("/utc-time");
expect(res.status).toBe(200);
expect(res.body).toHaveProperty("utc_time", mockTime);
});
it("returns 503 when database query fails", async () => {
getUtcTimestampSpy.mockResolvedValueOnce(null);
const res = await request(app).get("/utc-time");
expect(res.status).toBe(503);
expect(res.body).toHaveProperty("error");
});
});

View file

@ -1,6 +1,7 @@
import express from "express";
import { hostname } from "node:os";
import { greeting } from "./greeting.js";
import { getUtcTimestamp } from "./services/database.js";
const app = express();
const port = Number(process.env.PORT ?? 3000);
@ -20,4 +21,13 @@ app.get("/", (_req, res) => {
});
});
app.get("/utc-time", async (_req, res) => {
const utcTime = await getUtcTimestamp();
if (!utcTime) {
res.status(503).json({ error: "Database not configured or unavailable" });
return;
}
res.json({ utc_time: utcTime });
});
export { app, port };

View file

@ -0,0 +1,9 @@
-- Migration: Create stored procedure to get UTC timestamp
-- Run: psql -f 001_create_utc_timestamp_function.sql
CREATE OR REPLACE FUNCTION get_utc_timestamp()
RETURNS TIMESTAMP WITH TIME ZONE AS $$
BEGIN
RETURN NOW();
END;
$$ LANGUAGE plpgsql;

15
src/services/database.ts Normal file
View file

@ -0,0 +1,15 @@
import { sequelize } from "../config/database.js";
export async function getUtcTimestamp(): Promise<string | null> {
if (!sequelize) {
return null;
}
try {
const [results] = await sequelize.query("SELECT NOW() AS utc_time");
const rows = results as { utc_time: string }[];
return rows[0]?.utc_time ?? null;
} catch {
return null;
}
}