@commonpub/layer 0.3.23 → 0.3.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.3.23",
3
+ "version": "0.3.24",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -44,15 +44,15 @@
44
44
  "vue": "^3.4.0",
45
45
  "vue-router": "^4.3.0",
46
46
  "zod": "^4.3.6",
47
- "@commonpub/config": "0.7.1",
48
47
  "@commonpub/auth": "0.5.0",
49
- "@commonpub/editor": "0.5.0",
50
- "@commonpub/protocol": "0.9.5",
51
48
  "@commonpub/docs": "0.5.2",
49
+ "@commonpub/protocol": "0.9.5",
50
+ "@commonpub/editor": "0.5.0",
52
51
  "@commonpub/schema": "0.8.12",
52
+ "@commonpub/server": "2.15.0",
53
+ "@commonpub/config": "0.7.1",
53
54
  "@commonpub/ui": "0.7.1",
54
- "@commonpub/learning": "0.5.0",
55
- "@commonpub/server": "2.15.0"
55
+ "@commonpub/learning": "0.5.0"
56
56
  },
57
57
  "scripts": {}
58
58
  }
@@ -10,17 +10,24 @@ export default defineEventHandler(async (event) => {
10
10
  const user = requireAuth(event);
11
11
  const { url, purpose } = await parseBody(event, schema);
12
12
 
13
- // SSRF protection — block private IPs
13
+ // SSRF protection — block private/internal IPs
14
14
  const parsed = new URL(url);
15
- const hostname = parsed.hostname;
15
+ const hostname = parsed.hostname.toLowerCase();
16
+ const h = hostname.replace(/^\[|\]$/g, '');
16
17
  if (
17
- hostname === 'localhost' ||
18
- hostname === '127.0.0.1' ||
19
- hostname === '::1' ||
20
- hostname.startsWith('10.') ||
21
- hostname.startsWith('192.168.') ||
22
- hostname.match(/^172\.(1[6-9]|2\d|3[01])\./) ||
23
- hostname === '169.254.169.254' // AWS metadata
18
+ h === 'localhost' ||
19
+ h === 'localhost.localdomain' ||
20
+ h === 'metadata.google.internal' ||
21
+ h.endsWith('.local') ||
22
+ /^127\./.test(h) ||
23
+ /^10\./.test(h) ||
24
+ /^172\.(1[6-9]|2\d|3[01])\./.test(h) ||
25
+ /^192\.168\./.test(h) ||
26
+ /^169\.254\./.test(h) ||
27
+ /^0\./.test(h) ||
28
+ h === '::1' ||
29
+ /^f[cd]/i.test(h) ||
30
+ /^fe80/i.test(h)
24
31
  ) {
25
32
  throw createError({ statusCode: 400, statusMessage: 'Cannot fetch from private/local addresses' });
26
33
  }
@@ -29,15 +29,22 @@ export default defineEventHandler(async (event) => {
29
29
  }
30
30
 
31
31
  // Block localhost/private IPs (SSRF prevention)
32
- const hostname = parsed.hostname;
32
+ const hostname = parsed.hostname.toLowerCase();
33
+ const h = hostname.replace(/^\[|\]$/g, ''); // strip IPv6 brackets
33
34
  if (
34
- hostname === 'localhost' ||
35
- hostname === '127.0.0.1' ||
36
- hostname === '::1' ||
37
- hostname.startsWith('10.') ||
38
- hostname.startsWith('172.') ||
39
- hostname.startsWith('192.168.') ||
40
- hostname.endsWith('.local')
35
+ h === 'localhost' ||
36
+ h === 'localhost.localdomain' ||
37
+ h === 'metadata.google.internal' ||
38
+ h.endsWith('.local') ||
39
+ /^127\./.test(h) ||
40
+ /^10\./.test(h) ||
41
+ /^172\.(1[6-9]|2\d|3[01])\./.test(h) ||
42
+ /^192\.168\./.test(h) ||
43
+ /^169\.254\./.test(h) ||
44
+ /^0\./.test(h) ||
45
+ h === '::1' ||
46
+ /^f[cd]/i.test(h) ||
47
+ /^fe80/i.test(h)
41
48
  ) {
42
49
  throw createError({ statusCode: 403, statusMessage: 'Private addresses not allowed' });
43
50
  }
@@ -30,8 +30,12 @@ export default defineEventHandler(async (event) => {
30
30
  controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'counts', notifications, messages })}\n\n`));
31
31
  }
32
32
 
33
- // Send initial counts
34
- await sendCounts();
33
+ // Send initial counts — if DB is unavailable, send zeros and let polling retry
34
+ try {
35
+ await sendCounts();
36
+ } catch {
37
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'counts', notifications: 0, messages: 0 })}\n\n`));
38
+ }
35
39
 
36
40
  // Poll every 10 seconds
37
41
  const interval = setInterval(async () => {
@@ -39,7 +39,11 @@ export default defineNitroPlugin((nitro) => {
39
39
  // Run first sync after a brief delay to avoid startup contention
40
40
  runSync(domain, intervalMs, backfillOnSync);
41
41
 
42
- interval = setInterval(() => runSync(domain, intervalMs, backfillOnSync), intervalMs);
42
+ interval = setInterval(() => {
43
+ runSync(domain, intervalMs, backfillOnSync).catch((err) => {
44
+ console.error('[hub-sync] Sync worker unexpected error:', err instanceof Error ? err.message : err);
45
+ });
46
+ }, intervalMs);
43
47
  } catch (err) {
44
48
  console.error('[hub-sync] Failed to start:', err instanceof Error ? err.message : err);
45
49
  }
@@ -64,7 +64,11 @@ export default defineNitroPlugin((nitro) => {
64
64
 
65
65
  // Digest scheduler — runs every hour, sends digests for users whose digest window has elapsed
66
66
  const DIGEST_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
67
- digestInterval = setInterval(() => runDigest(siteUrl, siteName), DIGEST_INTERVAL_MS);
67
+ digestInterval = setInterval(() => {
68
+ runDigest(siteUrl, siteName).catch((err) => {
69
+ console.error('[notification-email] Digest scheduler unexpected error:', err instanceof Error ? err.message : err);
70
+ });
71
+ }, DIGEST_INTERVAL_MS);
68
72
 
69
73
  console.log('[notification-email] Digest scheduler started (interval: 1h)');
70
74
  } catch (err) {