initial commit
This commit is contained in:
commit
46911314f2
16 changed files with 1733 additions and 0 deletions
22
src/db.ts
Normal file
22
src/db.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { Pool } from 'pg';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export const pgPool = new Pool({
|
||||
host: process.env.POSTGRES_HOST,
|
||||
port: Number(process.env.POSTGRES_PORT || 5432),
|
||||
database: process.env.POSTGRES_DB,
|
||||
user: process.env.POSTGRES_USER,
|
||||
password: process.env.POSTGRES_PASSWORD,
|
||||
});
|
||||
|
||||
export async function initDb(): Promise<void> {
|
||||
await pgPool.query(`
|
||||
CREATE TABLE IF NOT EXISTS motd (
|
||||
id SERIAL PRIMARY KEY,
|
||||
message TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
}
|
||||
85
src/http/motd.ts
Normal file
85
src/http/motd.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { Express } from 'express';
|
||||
import { redisClient } from '../index.js';
|
||||
import { pgPool } from '../db.js';
|
||||
|
||||
|
||||
export async function httpMotd(app: Express) {
|
||||
|
||||
app.get('/motd', async (_req, res) => {
|
||||
try {
|
||||
const cached = await redisClient.get('motd:current');
|
||||
if (cached) {
|
||||
return res.json({
|
||||
source: 'redis',
|
||||
message: cached,
|
||||
});
|
||||
}
|
||||
|
||||
const result = await pgPool.query(
|
||||
`
|
||||
SELECT message
|
||||
FROM motd
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT 1
|
||||
`
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
return res.status(404).json({
|
||||
error: 'No MOTD set',
|
||||
});
|
||||
}
|
||||
|
||||
const message = result.rows[0].message as string;
|
||||
await redisClient.set('motd:current', message, {
|
||||
EX: 60,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
source: 'postgres',
|
||||
message,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/motd', async (req, res) => {
|
||||
try {
|
||||
const message = req.body?.message;
|
||||
|
||||
if (typeof message !== 'string' || message.trim() === '') {
|
||||
return res.status(400).json({
|
||||
error: 'message must be a non-empty string',
|
||||
});
|
||||
}
|
||||
|
||||
const trimmedMessage = message.trim();
|
||||
|
||||
await pgPool.query(
|
||||
`
|
||||
INSERT INTO motd (message)
|
||||
VALUES ($1)
|
||||
`,
|
||||
[trimmedMessage]
|
||||
);
|
||||
|
||||
await redisClient.set('motd:current', trimmedMessage, {
|
||||
EX: 60,
|
||||
});
|
||||
|
||||
return res.status(201).json({
|
||||
message: 'MOTD updated',
|
||||
motd: trimmedMessage,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
36
src/index.ts
Normal file
36
src/index.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import dotenv from 'dotenv';
|
||||
import express from 'express';
|
||||
import { createClient } from 'redis';
|
||||
import { initDb } from './db.js';
|
||||
import { httpMotd } from './http/motd.js';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
const port = Number(3000);
|
||||
|
||||
export const redisClient = createClient({
|
||||
url: process.env.REDIS_URL,
|
||||
});
|
||||
|
||||
redisClient.on('error', (err) => {
|
||||
console.error('Redis error:', err);
|
||||
});
|
||||
|
||||
async function main(): Promise<void> {
|
||||
await redisClient.connect();
|
||||
await initDb();
|
||||
|
||||
await httpMotd(app);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Failed to start app:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue