@lexho111/plainblog 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/server.js CHANGED
@@ -45,15 +45,21 @@ async function readBody(req, timeout = 15000, maxSize = 1024 * 1024) {
45
45
  }
46
46
  });
47
47
 
48
- req.on("end", () => {
48
+ req.on("end", function read_body_end() {
49
49
  if (!completed) {
50
50
  completed = true;
51
51
  clearTimeout(timer);
52
- resolve(Buffer.concat(chunks).toString());
52
+ if (chunks.length === 0) {
53
+ resolve("");
54
+ } else if (chunks.length === 1) {
55
+ resolve(chunks[0].toString());
56
+ } else {
57
+ resolve(Buffer.concat(chunks).toString());
58
+ }
53
59
  }
54
60
  const time_end = performance.now();
55
61
  const duration = time_end - time_start;
56
- debug_perf("readBody on end took " + duration + "ms", duration);
62
+ debug_perf("readBody on end", duration);
57
63
  });
58
64
 
59
65
  req.on("error", (err) => {
@@ -64,7 +70,7 @@ async function readBody(req, timeout = 15000, maxSize = 1024 * 1024) {
64
70
  }
65
71
  const time_end = performance.now();
66
72
  const duration = time_end - time_start;
67
- debug_perf("readBody on error took " + duration + "ms", duration);
73
+ debug_perf("readBody on error", duration);
68
74
  });
69
75
  });
70
76
  }
@@ -106,37 +112,55 @@ export async function createServer(
106
112
  res.on("finish", clearTimeouts);
107
113
  res.on("close", clearTimeouts);
108
114
  //debug("query %s", req.url);
109
- await api(req, res, async (req, res) => {
110
- const time_start = performance.now();
111
- await jsonAPI(
112
- req,
113
- res,
114
- title,
115
- articles,
116
- databaseModel,
117
- readBody,
118
- auth.isAuthenticated.bind(auth),
119
- );
120
- const time_end = performance.now();
121
- const duration = time_end - time_start;
122
- debug_perf1("jsonAPI took " + duration + "ms", duration, 400);
123
- });
124
- if (res.headersSent) return;
125
115
 
126
- await login(
127
- req,
128
- res,
129
- async (req, res) => {
130
- await auth.handleLogin(
116
+ // Helper: Führt eine Route aus und stoppt, wenn eine Antwort gesendet wurde
117
+ const tryRoute = async (routeCall) => {
118
+ if (res.headersSent) return true;
119
+ const p = routeCall();
120
+ if (p) {
121
+ await p;
122
+ return res.headersSent;
123
+ }
124
+ return false;
125
+ };
126
+
127
+ if (
128
+ await tryRoute(() =>
129
+ api(req, res, async function handleAPIRequest(req, res) {
130
+ const time_start = performance.now();
131
+ await jsonAPI(
132
+ req,
133
+ res,
134
+ title,
135
+ articles,
136
+ databaseModel,
137
+ readBody,
138
+ auth.isAuthenticated.bind(auth),
139
+ );
140
+ const time_end = performance.now();
141
+ const duration = time_end - time_start;
142
+ debug_perf1("jsonAPI", duration, 400);
143
+ }),
144
+ )
145
+ )
146
+ return;
147
+
148
+ if (
149
+ await tryRoute(() =>
150
+ login(
131
151
  req,
132
152
  res,
133
- readBody,
134
- REQUEST_TIMEOUT,
135
- MAX_REQUEST_SIZE,
136
- );
137
- },
138
- async (req, res) => {
139
- res.end(`${header("My Blog")}<body>
153
+ async (req, res) => {
154
+ await auth.handleLogin(
155
+ req,
156
+ res,
157
+ readBody,
158
+ REQUEST_TIMEOUT,
159
+ MAX_REQUEST_SIZE,
160
+ );
161
+ },
162
+ async (req, res) => {
163
+ res.end(`${header("My Blog")}<body>
140
164
  <form class="loginform" id="loginForm">
141
165
  <h1>Blog</h1>
142
166
  <!-- Message container -->
@@ -183,130 +207,144 @@ export async function createServer(
183
207
  });
184
208
  </script>
185
209
  </body></html>`);
186
- },
187
- );
188
- if (res.headersSent) return;
189
-
190
- await logout(req, res, (req, res) => {
191
- auth.handleLogout(req, res);
192
- });
193
- if (res.headersSent) return;
194
-
195
- await new1(req, res, async (req, res) => {
196
- if (!auth.isAuthenticated(req)) {
197
- debug("not authenticated");
198
- res.writeHead(403, { "Content-Type": "application/json" });
199
- res.end(JSON.stringify({ error: "Forbidden" }));
200
- return;
201
- }
202
-
203
- try {
204
- const body = await readBody(req);
205
- const params = new URLSearchParams(body);
206
- const articleData = Object.fromEntries(params.entries());
207
-
208
- debug("New Article Data:", articleData);
209
- // local
210
- // Write-before-exit strategy: Create in memory, save on closeServer()
211
- const savedArticle = Article.createNew(
212
- articleData.title,
213
- articleData.content,
214
- );
210
+ },
211
+ ),
212
+ )
213
+ )
214
+ return;
215
215
 
216
- databaseModel
217
- .save(savedArticle)
218
- .catch((err) => console.error("Async save failed:", err));
216
+ if (
217
+ await tryRoute(() =>
218
+ logout(req, res, (req, res) => {
219
+ auth.handleLogout(req, res);
220
+ }),
221
+ )
222
+ )
223
+ return;
219
224
 
220
- await postArticle(savedArticle);
221
- // external
225
+ if (
226
+ await tryRoute(() =>
227
+ new1(req, res, async (req, res) => {
228
+ if (!auth.isAuthenticated(req)) {
229
+ debug("not authenticated");
230
+ res.writeHead(403, { "Content-Type": "application/json" });
231
+ res.end(JSON.stringify({ error: "Forbidden" }));
232
+ return;
233
+ }
222
234
 
223
- // Success response
224
- res.writeHead(302, { Location: "/" });
225
- res.end();
226
- } catch (formErr) {
227
- debug("Error handling form submission: %s", formErr.message);
228
- if (!res.headersSent) {
229
- res.writeHead(400, { "Content-Type": "application/json" });
230
- res.end(JSON.stringify({ error: "Failed to parse form data" }));
231
- }
232
- }
233
- });
234
- if (res.headersSent) return;
235
-
236
- await getPages(
237
- req,
238
- res,
239
- async (req, res) => {
240
- // reload styles and scripts on (every) request
241
- if (assetManager.reloadStylesOnGET) {
242
- // This is a development-only feature and a major performance risk.
243
- if (process.env.NODE_ENV === "production") {
244
- console.warn(
245
- "Warning: 'reloadStylesOnGET' is enabled in a production-like environment. This is a major performance risk and should be disabled.",
235
+ try {
236
+ const body = await readBody(req);
237
+ const params = new URLSearchParams(body);
238
+ const articleData = Object.fromEntries(params.entries());
239
+
240
+ debug("New Article Data:", articleData);
241
+ // local
242
+ // Write-before-exit strategy: Create in memory, save on closeServer()
243
+ const savedArticle = Article.createNew(
244
+ articleData.title,
245
+ articleData.content,
246
246
  );
247
- assetManager.reloadStylesOnGET = false; // Disable it for subsequent requests
248
- }
249
- await assetManager.reload();
250
- }
251
247
 
252
- let loggedin = false;
253
- if (!auth.isAuthenticated(req)) {
254
- // login
255
- loggedin = false;
256
- } else {
257
- // logout
258
- loggedin = true;
259
- }
248
+ databaseModel
249
+ .save(savedArticle)
250
+ .catch((err) => console.error("Async save failed:", err));
260
251
 
261
- if (angular) {
262
- // use angular frontend
263
- const filePath = path.join(publicDir, "index.html");
252
+ await postArticle(savedArticle);
253
+ // external
264
254
 
265
- debug("%s", filePath);
266
- try {
267
- const data = await readFile(filePath);
268
- // Manual MIME type detection (simplified)
269
- const ext = path.extname(filePath);
270
- const mimeTypes = {
271
- ".html": "text/html",
272
- ".css": "text/css",
273
- ".js": "text/javascript",
274
- };
275
-
276
- res.writeHead(200, {
277
- "Content-Type": mimeTypes[ext] || "application/octet-stream",
278
- });
279
- res.end(data);
280
- } catch (fileErr) {
281
- debug("Error reading index file: %s", fileErr.message);
255
+ // Success response
256
+ res.writeHead(302, { Location: "/" });
257
+ res.end();
258
+ } catch (formErr) {
259
+ debug("Error handling form submission: %s", formErr.message);
282
260
  if (!res.headersSent) {
283
- debug404("unmatched route: %s %s", req.method, req.url);
284
- res.writeHead(404);
285
- return res.end("Index-File Not Found");
261
+ res.writeHead(400, { "Content-Type": "application/json" });
262
+ res.end(JSON.stringify({ error: "Failed to parse form data" }));
286
263
  }
287
264
  }
288
- } else {
289
- // use built in view engine
290
- try {
291
- const html = await html_content(loggedin); // render this blog to HTML
292
- res.writeHead(200, {
293
- "Content-Type": "text/html; charset=UTF-8",
294
- });
295
- res.end(html);
296
- return;
297
- } catch (renderErr) {
298
- console.error("Error rendering HTML:", renderErr);
299
- if (!res.headersSent) {
300
- res.writeHead(500, { "Content-Type": "text/plain" });
301
- res.end("Internal Server Error");
265
+ }),
266
+ )
267
+ )
268
+ return;
269
+
270
+ if (
271
+ await tryRoute(() =>
272
+ getPages(
273
+ req,
274
+ res,
275
+ async (req, res) => {
276
+ // reload styles and scripts on (every) request
277
+ if (assetManager.reloadStylesOnGET) {
278
+ // This is a development-only feature and a major performance risk.
279
+ if (process.env.NODE_ENV === "production") {
280
+ console.warn(
281
+ "Warning: 'reloadStylesOnGET' is enabled in a production-like environment. This is a major performance risk and should be disabled.",
282
+ );
283
+ assetManager.reloadStylesOnGET = false; // Disable it for subsequent requests
284
+ }
285
+ await assetManager.reload();
286
+ }
287
+
288
+ let loggedin = false;
289
+ if (!auth.isAuthenticated(req)) {
290
+ // login
291
+ loggedin = false;
292
+ } else {
293
+ // logout
294
+ loggedin = true;
302
295
  }
303
- return;
304
- }
305
- }
306
- },
307
- publicDir,
308
- );
309
- if (res.headersSent) return;
296
+
297
+ if (angular) {
298
+ // use angular frontend
299
+ const filePath = path.join(publicDir, "index.html");
300
+
301
+ debug("%s", filePath);
302
+ try {
303
+ const data = await readFile(filePath);
304
+ // Manual MIME type detection (simplified)
305
+ const ext = path.extname(filePath);
306
+ const mimeTypes = {
307
+ ".html": "text/html",
308
+ ".css": "text/css",
309
+ ".js": "text/javascript",
310
+ };
311
+
312
+ res.writeHead(200, {
313
+ "Content-Type": mimeTypes[ext] || "application/octet-stream",
314
+ });
315
+ res.end(data);
316
+ } catch (fileErr) {
317
+ debug("Error reading index file: %s", fileErr.message);
318
+ if (!res.headersSent) {
319
+ debug404("unmatched route: %s %s", req.method, req.url);
320
+ res.writeHead(404);
321
+ return res.end("Index-File Not Found");
322
+ }
323
+ }
324
+ } else {
325
+ // use built in view engine
326
+ try {
327
+ const html = await html_content(loggedin); // render this blog to HTML
328
+ res.writeHead(200, {
329
+ "Content-Type": "text/html; charset=UTF-8",
330
+ });
331
+ res.end(html);
332
+ return;
333
+ } catch (renderErr) {
334
+ console.error("Error rendering HTML:", renderErr);
335
+ if (!res.headersSent) {
336
+ res.writeHead(500, { "Content-Type": "text/plain" });
337
+ res.end("Internal Server Error");
338
+ }
339
+ return;
340
+ }
341
+ }
342
+ },
343
+ publicDir,
344
+ ),
345
+ )
346
+ )
347
+ return;
310
348
 
311
349
  // If no route was matched, send a 404
312
350
  if (!res.headersSent) {
@@ -472,7 +510,7 @@ async function jsonAPI(
472
510
  const dbArticles = await articles.findAll(start, end, limit);
473
511
  const time_end = performance.now();
474
512
  const duration = time_end - time_start;
475
- debug_perf('GET "/api/articles" took ' + duration + "ms", duration);
513
+ debug_perf('GET "/api/articles"', duration);
476
514
  if (!res.headersSent) {
477
515
  res.writeHead(200, { "Content-Type": "application/json" });
478
516
  const responseData = {
@@ -502,13 +540,10 @@ async function jsonAPI(
502
540
  body = await readBody(req, REQUEST_TIMEOUT, MAX_REQUEST_SIZE);
503
541
  const time_end1 = performance.now();
504
542
  const duration1 = time_end1 - time_start1;
505
- debug_perf(
506
- 'POST "/api/articles" readBody took ' + duration1 + "ms",
507
- duration1,
508
- );
543
+ debug_perf('POST "/api/articles" readBody', duration1);
509
544
  const time_end = performance.now();
510
545
  const duration = time_end - time_start;
511
- debug_perf('POST "/api/articles" took ' + duration + "ms", duration);
546
+ debug_perf('POST "/api/articles"', duration);
512
547
  } catch (err) {
513
548
  debug("Error reading request body: %s", err.message);
514
549
  if (!res.headersSent) {
@@ -541,7 +576,7 @@ async function jsonAPI(
541
576
  debug("new article: %s", newArticle.title);
542
577
  const time_end1 = performance.now();
543
578
  const duration1 = time_end1 - time_start;
544
- debug_perf("new article took " + duration1 + "ms", duration1);
579
+ debug_perf("new article took ", duration1);
545
580
 
546
581
  // Create article with a temporary ID for immediate use
547
582
  const articleWithTempId = Article.createNew(
@@ -563,7 +598,7 @@ async function jsonAPI(
563
598
 
564
599
  const time_end1 = performance.now();
565
600
  const duration = time_end1 - time_start;
566
- debug_perf("databaseModel took " + duration + "ms", duration);
601
+ debug_perf("databaseModel", duration);
567
602
  } catch (dbErr) {
568
603
  console.error("Database save error:", dbErr.message);
569
604
  console.error("Stack:", dbErr.stack);
@@ -636,7 +671,7 @@ async function jsonAPI(
636
671
  }
637
672
  const time_end = performance.now();
638
673
  const duration = time_end - time_start;
639
- debug_perf('DELETE "/api/articles" took ' + duration + "ms", duration);
674
+ debug_perf('DELETE "/api/articles"', duration);
640
675
  } else if (req.method === "PUT") {
641
676
  debug("PUT an article");
642
677
  if (!isAuthenticated(req)) {
@@ -698,7 +733,7 @@ async function jsonAPI(
698
733
  }
699
734
  const time_end = performance.now();
700
735
  const duration = time_end - time_start;
701
- debug_perf('POST "/api/articles" took ' + duration + "ms", duration);
736
+ debug_perf('POST "/api/articles"', duration);
702
737
  } else {
703
738
  if (!res.headersSent) {
704
739
  debug404("unmatched route: %s %s", req.method, req.url);
package/src/styles.css CHANGED
@@ -19,7 +19,7 @@ html {
19
19
  body {
20
20
  color: var(--text-primary);
21
21
  background: var(--white_darker);
22
- font-family: Arial;
22
+ font-family: Arial; /* */
23
23
  }
24
24
  nav {
25
25
  margin-top: 10px;
package/styles.hash ADDED
@@ -0,0 +1 @@
1
+ d7f416eb85f42f3fe51ac52dc717c448
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ // Visit https://aka.ms/tsconfig to read more about this file
3
+ "compilerOptions": {
4
+ "target": "ES2021",
5
+ "module": "es2020",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true
10
+ },
11
+ "include": ["Article.ts"]
12
+ }