-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.ts
More file actions
129 lines (111 loc) · 3.52 KB
/
main.ts
File metadata and controls
129 lines (111 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import express from 'express';
import path from 'path';
import { engine } from 'express-handlebars';
import Handlebars from 'handlebars';
import Keyv from 'keyv';
import { KeyvTigris } from '@tigrisdata/keyv-tigris';
import { uuidv7 } from "uuidv7";
import { body, matchedData, validationResult } from "express-validator";
import { Paste, createPaste } from './models';
import 'dotenv/config';
const app = express();
const PORT = process.env.PORT || 3333;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Serve static files
app.use(express.static(path.join(__dirname, 'public')));
// Set view engine
app.engine('handlebars', engine());
app.set('view engine', 'handlebars');
app.set('views', path.join(__dirname, 'views'));
// Register RFC 3339 date formatting helper
Handlebars.registerHelper('formatRFC3339', function (date) {
if (!date) return '';
try {
// If date is a string, convert to Date
const d = typeof date === 'string' ? new Date(date) : date;
return d.toISOString();
} catch {
return String(date);
}
});
const store = new KeyvTigris();
const pastes = new Keyv<Paste>({ store, namespace: "paste" });
pastes.on('error', err => console.error("Store error:", err));
// Routes
app.get('/', (req: express.Request, res: express.Response) => {
if (req.headers['hx-request'] === 'true') {
res.render('index', { layout: false });
} else {
res.render('index', {});
}
});
app.get('/paste/:id', async (req: express.Request, res: express.Response) => {
const { id } = req.params;
try {
const paste = await pastes.get(id);
if (!paste) {
return res.status(404).render('error', {
title: "Paste not found",
message: `No paste found for id ${id}`,
canRetry: false,
});
}
res.render('paste', { title: paste.title, paste });
} catch (err) {
console.error(err);
return res.status(500).render('error', {
title: "Internal server error",
message: "Can't fetch the paste.",
canRetry: true,
});
}
});
app.post('/submit', [
body('title').trim().notEmpty().withMessage('A title is required'),
body('paste').notEmpty().withMessage('A body is required'),
], async (req: express.Request, res: express.Response) => {
if (req.headers['hx-request'] !== 'true') {
return res.status(400).render('error', {
title: "Invalid submission",
message: "Your form submission is invalid.",
canRetry: false,
});
}
const errs = validationResult(req);
if (!errs.isEmpty()) {
return res.status(400).render('error', {
title: "Invalid submission",
message: "Your form submission failed validation.",
details: errs.array().map((err) => {
return `${err.msg}`;
})
});
}
try {
const id = uuidv7();
const data = matchedData(req);
const paste = createPaste(id, data.title, data.paste);
if (!await pastes.set(id, paste)) {
return res.status(500).render('error', {
title: "Internal server error",
message: "Can't save the paste to Tigris.",
canRetry: true,
});
}
console.log("made new paste", { paste });
res.set('Hx-Push-Url', `/paste/${id}`);
res.render('paste', { paste, layout: false });
} catch (err) {
console.error(`got error: ${err}`);
return res.status(500).render('error', {
title: "Internal server error",
message: "Can't save the paste to Tigris.",
canRetry: true,
});
}
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});