fedbox 0.0.5 → 0.0.6

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/lib/server.js CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { createServer } from 'http'
8
8
  import { readFileSync, existsSync } from 'fs'
9
+ import { join, extname } from 'path'
9
10
  import { profile, auth, webfinger, outbox } from 'microfed'
10
11
  import {
11
12
  initStore,
@@ -383,7 +384,7 @@ async function handleRequest(req, res) {
383
384
  version: '2.1',
384
385
  software: {
385
386
  name: 'fedbox',
386
- version: '0.0.5',
387
+ version: '0.0.6',
387
388
  repository: 'https://github.com/micro-fed/fedbox'
388
389
  },
389
390
  protocols: ['activitypub'],
@@ -523,6 +524,25 @@ async function handleRequest(req, res) {
523
524
  return res.end(renderHome())
524
525
  }
525
526
 
527
+ // Static files from /public/
528
+ if (path.startsWith('/public/')) {
529
+ const filePath = join(process.cwd(), path)
530
+ if (existsSync(filePath)) {
531
+ const ext = extname(filePath)
532
+ const mimeTypes = {
533
+ '.js': 'application/javascript',
534
+ '.css': 'text/css',
535
+ '.html': 'text/html',
536
+ '.json': 'application/json',
537
+ '.png': 'image/png',
538
+ '.jpg': 'image/jpeg',
539
+ '.svg': 'image/svg+xml'
540
+ }
541
+ res.setHeader('Content-Type', mimeTypes[ext] || 'text/plain')
542
+ return res.end(readFileSync(filePath))
543
+ }
544
+ }
545
+
526
546
  res.writeHead(404)
527
547
  res.end('Not found')
528
548
  }
@@ -576,7 +596,7 @@ function renderProfile() {
576
596
  <title>${config.displayName} (@${config.username}@${getDomain()})</title>
577
597
  <meta name="viewport" content="width=device-width, initial-scale=1">
578
598
  <link rel="alternate" type="application/activity+json" href="${profileUrl}">
579
- <script type="application/ld+json">
599
+ <script type="application/ld+json" id="profile">
580
600
  ${JSON.stringify(actor, null, 2)}
581
601
  </script>
582
602
  <style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fedbox",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Zero to Fediverse in 60 seconds",
5
5
  "type": "module",
6
6
  "main": "lib/server.js",
@@ -12,8 +12,8 @@
12
12
  "test": "node --test 'test/*.test.js'"
13
13
  },
14
14
  "dependencies": {
15
- "microfed": "^0.0.13",
16
- "better-sqlite3": "^11.0.0"
15
+ "better-sqlite3": "^11.0.0",
16
+ "microfed": "^0.0.14"
17
17
  },
18
18
  "keywords": [
19
19
  "activitypub",
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Microfed Profile Component
3
+ * Reads JSON-LD data island and renders a profile card
4
+ *
5
+ * Usage: <script src="profile.js"></script>
6
+ * Requires: <script type="application/ld+json" id="profile">...</script>
7
+ */
8
+ (function() {
9
+ 'use strict'
10
+
11
+ function init() {
12
+ // Find JSON-LD data island
13
+ const script = document.querySelector('script[type="application/ld+json"]#profile') ||
14
+ document.querySelector('script[type="application/ld+json"]')
15
+
16
+ if (!script) {
17
+ console.warn('Microfed Profile: No JSON-LD data island found')
18
+ return
19
+ }
20
+
21
+ let actor
22
+ try {
23
+ actor = JSON.parse(script.textContent)
24
+ } catch (e) {
25
+ console.error('Microfed Profile: Invalid JSON-LD', e)
26
+ return
27
+ }
28
+
29
+ // Find or create container
30
+ let container = document.getElementById('microfed-profile')
31
+ if (!container) {
32
+ container = document.createElement('div')
33
+ container.id = 'microfed-profile'
34
+ script.parentNode.insertBefore(container, script.nextSibling)
35
+ }
36
+
37
+ render(container, actor)
38
+ }
39
+
40
+ function render(container, actor) {
41
+ const name = actor.name || actor.preferredUsername || 'Unknown'
42
+ const username = actor.preferredUsername || ''
43
+ const summary = actor.summary || ''
44
+ const icon = actor.icon?.url || actor.icon || ''
45
+ const followers = actor.followers || ''
46
+ const following = actor.following || ''
47
+
48
+ container.innerHTML = `
49
+ <style>
50
+ .mf-profile {
51
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
52
+ max-width: 400px;
53
+ border: 1px solid #e1e4e8;
54
+ border-radius: 8px;
55
+ padding: 20px;
56
+ background: #fff;
57
+ }
58
+ .mf-profile-header {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 16px;
62
+ margin-bottom: 12px;
63
+ }
64
+ .mf-profile-avatar {
65
+ width: 64px;
66
+ height: 64px;
67
+ border-radius: 50%;
68
+ object-fit: cover;
69
+ background: #e1e4e8;
70
+ }
71
+ .mf-profile-avatar-placeholder {
72
+ width: 64px;
73
+ height: 64px;
74
+ border-radius: 50%;
75
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ color: white;
80
+ font-size: 24px;
81
+ font-weight: bold;
82
+ }
83
+ .mf-profile-name {
84
+ font-size: 1.25rem;
85
+ font-weight: 600;
86
+ margin: 0;
87
+ color: #24292e;
88
+ }
89
+ .mf-profile-username {
90
+ font-size: 0.875rem;
91
+ color: #586069;
92
+ margin: 2px 0 0 0;
93
+ }
94
+ .mf-profile-summary {
95
+ color: #24292e;
96
+ line-height: 1.5;
97
+ margin: 12px 0;
98
+ }
99
+ .mf-profile-stats {
100
+ display: flex;
101
+ gap: 16px;
102
+ margin-top: 12px;
103
+ padding-top: 12px;
104
+ border-top: 1px solid #e1e4e8;
105
+ }
106
+ .mf-profile-stat {
107
+ color: #586069;
108
+ font-size: 0.875rem;
109
+ text-decoration: none;
110
+ }
111
+ .mf-profile-stat:hover {
112
+ color: #0366d6;
113
+ }
114
+ .mf-profile-stat strong {
115
+ color: #24292e;
116
+ }
117
+ </style>
118
+ <div class="mf-profile">
119
+ <div class="mf-profile-header">
120
+ ${icon
121
+ ? `<img class="mf-profile-avatar" src="${escapeHtml(icon)}" alt="${escapeHtml(name)}">`
122
+ : `<div class="mf-profile-avatar-placeholder">${escapeHtml(name.charAt(0).toUpperCase())}</div>`
123
+ }
124
+ <div>
125
+ <h2 class="mf-profile-name">${escapeHtml(name)}</h2>
126
+ ${username ? `<p class="mf-profile-username">@${escapeHtml(username)}</p>` : ''}
127
+ </div>
128
+ </div>
129
+ ${summary ? `<div class="mf-profile-summary">${summary}</div>` : ''}
130
+ <div class="mf-profile-stats">
131
+ ${followers ? `<a class="mf-profile-stat" href="${escapeHtml(followers)}"><strong>Followers</strong></a>` : ''}
132
+ ${following ? `<a class="mf-profile-stat" href="${escapeHtml(following)}"><strong>Following</strong></a>` : ''}
133
+ </div>
134
+ </div>
135
+ `
136
+ }
137
+
138
+ function escapeHtml(str) {
139
+ if (!str) return ''
140
+ const div = document.createElement('div')
141
+ div.textContent = str
142
+ return div.innerHTML
143
+ }
144
+
145
+ // Initialize when DOM is ready
146
+ if (document.readyState === 'loading') {
147
+ document.addEventListener('DOMContentLoaded', init)
148
+ } else {
149
+ init()
150
+ }
151
+ })()