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 +22 -2
- package/package.json +3 -3
- package/public/profile.js +151 -0
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.
|
|
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.
|
|
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
|
-
"
|
|
16
|
-
"
|
|
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
|
+
})()
|