feat: pictures table, Hetzner S3 upload/delete, auto-migration

- pictures table with UUID, status enum, timestamps, blurhash, design
- Auto-trigger updates updated_at on every row change
- POST /api/pictures/:id/upload  → upload file to Hetzner snakkimo bucket
- DELETE /api/pictures/:id       → removes DB row + Hetzner file
- PATCH /api/pictures/:id        → auto-sets published/blocked timestamps
- Migration runs on every server start (idempotent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 13:39:16 +02:00
parent b82a468197
commit 0f35459b86
6 changed files with 1145 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ const express = require('express');
const cors = require('cors');
const auth = require('./middleware/auth');
const { pool } = require('./db');
const migrate = require('./db-migrate');
const app = express();
const PORT = process.env.PORT || 3000;
@@ -23,6 +24,7 @@ app.get('/health', async (req, res) => {
// Routes — protected by Bearer token
app.use('/api', auth, require('./routes/index'));
app.use('/api/pictures', auth, require('./routes/pictures'));
// 404
app.use((req, res) => {
@@ -35,6 +37,6 @@ app.use((err, req, res, next) => {
res.status(500).json({ error: 'Internal server error' });
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`snakkimo-API running on port ${PORT}`);
});
migrate()
.then(() => app.listen(PORT, '0.0.0.0', () => console.log(`snakkimo-API running on port ${PORT}`)))
.catch(err => { console.error('Migration failed:', err); process.exit(1); });