import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
export async function initDB() {
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
profile_image TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
user_id TEXT REFERENCES users(id),
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
`);
}
export async function upsertUser(id: string, username: string, image: string) {
return pool.query(
`INSERT INTO users (id, username, profile_image)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO UPDATE SET username = $2, profile_image = $3
RETURNING *`,
[id, username, image]
);
}
export async function createPost(userId: string, title: string, content: string) {
return pool.query(
'INSERT INTO posts (user_id, title, content) VALUES ($1, $2, $3) RETURNING *',
[userId, title, content]
);
}
export async function getPosts(limit = 20) {
return pool.query(
`SELECT p.*, u.username, u.profile_image
FROM posts p JOIN users u ON p.user_id = u.id
ORDER BY p.created_at DESC LIMIT $1`,
[limit]
);
}
export { pool };
import { Request, Response, NextFunction } from 'express';
import { upsertUser } from '../services/db';
export interface AuthedRequest extends Request {
user: { id: string; name: string; image: string };
}
export async function requireAuth(req: Request, res: Response, next: NextFunction) {
const userId = req.headers['x-replit-user-id'] as string;
if (!userId) return res.status(401).json({ error: 'Login required' });
const name = (req.headers['x-replit-user-name'] as string) || '';
const image = (req.headers['x-replit-user-profile-image'] as string) || '';
await upsertUser(userId, name, image);
(req as any).user = { id: userId, name, image };
next();
}
import { Router } from 'express';
import { requireAuth, AuthedRequest } from '../middleware/auth';
import { createPost, getPosts } from '../services/db';
import { Client as StorageClient } from '@replit/object-storage';
const router = Router();
const storage = new StorageClient();
router.get('/posts', async (req, res) => {
const { rows } = await getPosts();
res.json(rows);
});
router.post('/posts', requireAuth, async (req, res) => {
const { title, content } = req.body;
const user = (req as AuthedRequest).user;
const { rows } = await createPost(user.id, title, content);
res.status(201).json(rows[0]);
});
router.post('/upload', requireAuth, async (req, res) => {
const user = (req as AuthedRequest).user;
const filename = `uploads/${user.id}/${Date.now()}-${req.body.name}`;
await storage.uploadFromText(filename, req.body.content);
res.json({ path: filename });
});
export default router;
import express from 'express';
import { initDB, pool } from './services/db';
import apiRoutes from './routes/api';
const app = express();
app.use(express.json());
app.get('/health', async (req, res) => {
try {
await pool.query('SELECT 1');
res.json({ status: 'ok', uptime: process.uptime() });
} catch {
res.status(503).json({ status: 'degraded' });
}
});
app.get('/api/me', (req, res) => {
const id = req.headers['x-replit-user-id'];
if (!id) return res.json({ loggedIn: false });
res.json({
loggedIn: true,
id,
name: req.headers['x-replit-user-name'],
image: req.headers['x-replit-user-profile-image'],
});
});
app.use('/api', apiRoutes);
const PORT = parseInt(process.env.PORT || '3000');
initDB().then(() => {
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
});