javascript-solid-server 0.0.111 → 0.0.112
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/.claude/settings.local.json +2 -1
- package/README.md +11 -6
- package/bin/jss.js +1 -5
- package/docs/configuration.md +0 -37
- package/docs/invites.md +43 -0
- package/docs/mashlib.md +58 -0
- package/docs/nostr.md +56 -0
- package/docs/notifications.md +50 -0
- package/docs/quotas.md +36 -0
- package/package.json +1 -1
- package/src/auth/middleware.js +4 -7
- package/src/config.js +1 -6
- package/src/handlers/resource.js +7 -13
- package/src/server.js +11 -74
|
@@ -327,7 +327,8 @@
|
|
|
327
327
|
"Bash(gh label:*)",
|
|
328
328
|
"Bash(mongosh --eval \"db.runCommand\\({ ping: 1 }\\)\" 2>&1 | head -5)",
|
|
329
329
|
"Bash(which jss && jss --version 2>&1)",
|
|
330
|
-
"Bash(jss start --help 2>&1 | grep -i mongo)"
|
|
330
|
+
"Bash(jss start --help 2>&1 | grep -i mongo)",
|
|
331
|
+
"Bash(grep -A5 '\"\"files\"\"' package.json)"
|
|
331
332
|
]
|
|
332
333
|
}
|
|
333
334
|
}
|
package/README.md
CHANGED
|
@@ -91,23 +91,28 @@ Full options: [docs/configuration.md](docs/configuration.md)
|
|
|
91
91
|
|-------|------|
|
|
92
92
|
| Configuration & Options | [docs/configuration.md](docs/configuration.md) |
|
|
93
93
|
| Authentication | [docs/authentication.md](docs/authentication.md) |
|
|
94
|
+
| Mashlib / SolidOS UI | [docs/mashlib.md](docs/mashlib.md) |
|
|
95
|
+
| WebSocket Notifications | [docs/notifications.md](docs/notifications.md) |
|
|
94
96
|
| Git Support | [docs/git-support.md](docs/git-support.md) |
|
|
97
|
+
| Nostr Relay | [docs/nostr.md](docs/nostr.md) |
|
|
95
98
|
| ActivityPub & Mastodon API | [docs/activitypub.md](docs/activitypub.md) |
|
|
96
99
|
| remoteStorage | [docs/remotestorage.md](docs/remotestorage.md) |
|
|
97
|
-
| Security & Subdomain Mode | [docs/security.md](docs/security.md) |
|
|
98
|
-
| HTTP 402 Payments | [docs/payments.md](docs/payments.md) |
|
|
99
100
|
| WebRTC & Tunnel | [docs/webrtc.md](docs/webrtc.md) |
|
|
100
101
|
| MongoDB `/db/` Route | [docs/mongodb.md](docs/mongodb.md) |
|
|
102
|
+
| HTTP 402 Payments | [docs/payments.md](docs/payments.md) |
|
|
103
|
+
| Storage Quotas | [docs/quotas.md](docs/quotas.md) |
|
|
104
|
+
| Invite-Only Registration | [docs/invites.md](docs/invites.md) |
|
|
105
|
+
| Security & Subdomain Mode | [docs/security.md](docs/security.md) |
|
|
101
106
|
| Architecture & Structure | [docs/architecture.md](docs/architecture.md) |
|
|
102
107
|
|
|
103
108
|
## Comparison
|
|
104
109
|
|
|
105
110
|
| Server | Size | Deps | Notes |
|
|
106
111
|
|--------|------|------|-------|
|
|
107
|
-
| [JSS](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer) | ~
|
|
108
|
-
| [NSS](https://github.com/nodeSolidServer/node-solid-server) |
|
|
109
|
-
| [CSS](https://github.com/CommunitySolidServer/CommunitySolidServer) |
|
|
110
|
-
| [Pivot](https://github.com/solid-contrib/pivot) | ~
|
|
112
|
+
| [JSS](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer) | ~18K LoC | 15 | Minimal, JSON-LD native |
|
|
113
|
+
| [NSS](https://github.com/nodeSolidServer/node-solid-server) | ~25K LoC | 58 | Original Solid server |
|
|
114
|
+
| [CSS](https://github.com/CommunitySolidServer/CommunitySolidServer) | ~65K LoC | 70 | Modular, configurable |
|
|
115
|
+
| [Pivot](https://github.com/solid-contrib/pivot) | ~70K LoC | 70+ | Built on CSS |
|
|
111
116
|
|
|
112
117
|
## Performance
|
|
113
118
|
|
package/bin/jss.js
CHANGED
|
@@ -53,12 +53,10 @@ program
|
|
|
53
53
|
.option('--subdomains', 'Enable subdomain-based pods (XSS protection)')
|
|
54
54
|
.option('--no-subdomains', 'Disable subdomain-based pods')
|
|
55
55
|
.option('--base-domain <domain>', 'Base domain for subdomain pods (e.g., "example.com")')
|
|
56
|
-
.option('--mashlib', 'Enable Mashlib data browser (
|
|
57
|
-
.option('--mashlib-cdn', 'Enable Mashlib data browser (CDN mode, no local files needed)')
|
|
56
|
+
.option('--mashlib-cdn', 'Enable Mashlib data browser (CDN mode)')
|
|
58
57
|
.option('--mashlib-module <url>', 'Enable ES module data browser from a URL')
|
|
59
58
|
.option('--no-mashlib', 'Disable Mashlib data browser')
|
|
60
59
|
.option('--mashlib-version <version>', 'Mashlib version for CDN mode (default: 2.0.0)')
|
|
61
|
-
.option('--solidos-ui', 'Enable modern Nextcloud-style UI (requires --mashlib)')
|
|
62
60
|
.option('--git', 'Enable Git HTTP backend (clone/push support)')
|
|
63
61
|
.option('--no-git', 'Disable Git HTTP backend')
|
|
64
62
|
.option('--nostr', 'Enable Nostr relay')
|
|
@@ -143,7 +141,6 @@ program
|
|
|
143
141
|
mashlibCdn: config.mashlibCdn,
|
|
144
142
|
mashlibVersion: config.mashlibVersion,
|
|
145
143
|
mashlibModule: config.mashlibModule,
|
|
146
|
-
solidosUi: config.solidosUi,
|
|
147
144
|
git: config.git,
|
|
148
145
|
nostr: config.nostr,
|
|
149
146
|
nostrPath: config.nostrPath,
|
|
@@ -193,7 +190,6 @@ program
|
|
|
193
190
|
console.log(` Mashlib: local (data browser enabled)`);
|
|
194
191
|
}
|
|
195
192
|
if (config.mashlibModule) console.log(` Mashlib module: ${config.mashlibModule}`);
|
|
196
|
-
if (config.solidosUi) console.log(' SolidOS UI: enabled (modern interface)');
|
|
197
193
|
if (config.git) console.log(' Git: enabled (clone/push support)');
|
|
198
194
|
if (config.nostr) console.log(` Nostr: enabled (${config.nostrPath})`);
|
|
199
195
|
if (config.webrtc) console.log(` WebRTC: enabled (${config.webrtcPath || '/.webrtc'})`);
|
package/docs/configuration.md
CHANGED
|
@@ -355,43 +355,6 @@ jss quota reconcile alice
|
|
|
355
355
|
|
|
356
356
|
Supported formats: `50MB`, `1GB`, `500KB`, `1TB`
|
|
357
357
|
|
|
358
|
-
## Storage Quotas
|
|
359
|
-
|
|
360
|
-
Limit storage per pod to prevent abuse and manage resources:
|
|
361
|
-
|
|
362
|
-
```bash
|
|
363
|
-
jss start --default-quota 50MB
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Managing Quotas
|
|
367
|
-
|
|
368
|
-
```bash
|
|
369
|
-
# Set quota for a user (overrides default)
|
|
370
|
-
jss quota set alice 100MB
|
|
371
|
-
|
|
372
|
-
# Show quota info
|
|
373
|
-
jss quota show alice
|
|
374
|
-
# alice:
|
|
375
|
-
# Used: 12.5 MB
|
|
376
|
-
# Limit: 100 MB
|
|
377
|
-
# Free: 87.5 MB
|
|
378
|
-
# Usage: 12%
|
|
379
|
-
|
|
380
|
-
# Recalculate from actual disk usage
|
|
381
|
-
jss quota reconcile alice
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
### How It Works
|
|
385
|
-
|
|
386
|
-
- Quotas are tracked incrementally on PUT, POST, and DELETE operations
|
|
387
|
-
- When quota is exceeded, the server returns HTTP 507 Insufficient Storage
|
|
388
|
-
- Each pod stores its quota in `/{pod}/.quota.json`
|
|
389
|
-
- Use `reconcile` to fix quota drift from manual file changes
|
|
390
|
-
|
|
391
|
-
### Size Formats
|
|
392
|
-
|
|
393
|
-
Supported formats: `50MB`, `1GB`, `500KB`, `1TB`
|
|
394
|
-
|
|
395
358
|
### Mashlib Data Browser
|
|
396
359
|
|
|
397
360
|
Enable the [SolidOS Mashlib](https://github.com/SolidOS/mashlib) data browser for RDF resources. Two modes are available:
|
package/docs/invites.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Invite-Only Registration
|
|
2
|
+
|
|
3
|
+
Control who can create accounts by requiring invite codes.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
jss start --idp --invite-only
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Managing Invite Codes
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Create a single-use invite
|
|
13
|
+
jss invite create
|
|
14
|
+
# Created invite code: ABCD1234
|
|
15
|
+
|
|
16
|
+
# Create multi-use invite with note
|
|
17
|
+
jss invite create -u 5 -n "For team members"
|
|
18
|
+
|
|
19
|
+
# List all active invites
|
|
20
|
+
jss invite list
|
|
21
|
+
# CODE USES CREATED NOTE
|
|
22
|
+
# -------------------------------------------------------
|
|
23
|
+
# ABCD1234 0/1 2026-01-03
|
|
24
|
+
# EFGH5678 2/5 2026-01-03 For team members
|
|
25
|
+
|
|
26
|
+
# Revoke an invite
|
|
27
|
+
jss invite revoke ABCD1234
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## How It Works
|
|
31
|
+
|
|
32
|
+
| Mode | Registration | Pod Creation |
|
|
33
|
+
|------|--------------|--------------|
|
|
34
|
+
| Open (default) | Anyone can register | Anyone can create pods |
|
|
35
|
+
| Invite-only | Requires valid invite code | Via registration only |
|
|
36
|
+
|
|
37
|
+
When `--invite-only` is enabled:
|
|
38
|
+
- The registration page shows an "Invite Code" field
|
|
39
|
+
- Invalid or expired codes are rejected with an error
|
|
40
|
+
- Each use decrements the invite's remaining uses
|
|
41
|
+
- Depleted invites are automatically removed
|
|
42
|
+
|
|
43
|
+
Invite codes are stored in `.server/invites.json` in your data directory.
|
package/docs/mashlib.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Mashlib Data Browser
|
|
2
|
+
|
|
3
|
+
Enable the [SolidOS Mashlib](https://github.com/SolidOS/mashlib) data browser for RDF resources.
|
|
4
|
+
|
|
5
|
+
## Modes
|
|
6
|
+
|
|
7
|
+
**CDN Mode** (recommended for getting started):
|
|
8
|
+
```bash
|
|
9
|
+
jss start --mashlib-cdn --conneg
|
|
10
|
+
```
|
|
11
|
+
Loads mashlib from unpkg.com CDN. Zero footprint — no local files needed.
|
|
12
|
+
|
|
13
|
+
**Local Mode** (for production/offline):
|
|
14
|
+
```bash
|
|
15
|
+
jss start --mashlib --conneg
|
|
16
|
+
```
|
|
17
|
+
Serves mashlib from `src/mashlib-local/dist/`. Requires building mashlib locally:
|
|
18
|
+
```bash
|
|
19
|
+
cd src/mashlib-local
|
|
20
|
+
npm install && npm run build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**ES Module Mode** (for custom or next-gen mashlib builds):
|
|
24
|
+
```bash
|
|
25
|
+
jss start --mashlib-module https://example.com/mashlib.js
|
|
26
|
+
```
|
|
27
|
+
Loads an ES module-based data browser from any URL. Uses `<script type="module">` and `<div id="mashlib">` (self-initializing). CSS is auto-derived by replacing `.js` with `.css`. Content negotiation is auto-enabled.
|
|
28
|
+
|
|
29
|
+
## How It Works
|
|
30
|
+
|
|
31
|
+
1. Browser requests `/alice/public/data.ttl` with `Accept: text/html`
|
|
32
|
+
2. Server returns Mashlib HTML wrapper
|
|
33
|
+
3. Mashlib fetches the actual data via content negotiation
|
|
34
|
+
4. Mashlib renders an interactive, editable view
|
|
35
|
+
|
|
36
|
+
**Note:** Mashlib works best with `--conneg` enabled for Turtle support.
|
|
37
|
+
|
|
38
|
+
## Modern UI (SolidOS UI)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
jss start --mashlib --solidos-ui --conneg
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Serves a modern Nextcloud-style UI shell while reusing mashlib's data layer:
|
|
45
|
+
- Modern file browser with breadcrumb navigation
|
|
46
|
+
- Profile, Contacts, Sharing, and Settings views
|
|
47
|
+
- Path-based URLs (browser URL reflects current resource)
|
|
48
|
+
- Responsive design for mobile devices
|
|
49
|
+
|
|
50
|
+
Requires solidos-ui dist files in `src/mashlib-local/dist/solidos-ui/`. See [solidos-ui](https://github.com/solidos/solidos/tree/main/workspaces/solidos-ui) for details.
|
|
51
|
+
|
|
52
|
+
## Profile Pages
|
|
53
|
+
|
|
54
|
+
Pod profiles (`/alice/`) use HTML with embedded JSON-LD data islands and are rendered using:
|
|
55
|
+
- [mashlib-jss](https://github.com/JavaScriptSolidServer/mashlib-jss) — A fork of mashlib with `getPod()` fix for path-based pods
|
|
56
|
+
- [solidos-lite](https://github.com/SolidOS/solidos-lite) — Parses JSON-LD data islands into the RDF store
|
|
57
|
+
|
|
58
|
+
This allows profiles to work without server-side content negotiation while still providing full SolidOS editing capabilities.
|
package/docs/nostr.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Nostr Relay
|
|
2
|
+
|
|
3
|
+
Integrated NIP-01/NIP-11/NIP-16 Nostr relay running on the same port as the Solid server.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
jss start --nostr
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Endpoint
|
|
10
|
+
|
|
11
|
+
`wss://your.pod/relay` (configurable via `--nostr-path`)
|
|
12
|
+
|
|
13
|
+
## Supported NIPs
|
|
14
|
+
|
|
15
|
+
- **NIP-01** — Basic protocol flow (EVENT, REQ, CLOSE)
|
|
16
|
+
- **NIP-11** — Relay information document (`GET /relay` with `Accept: application/nostr+json`)
|
|
17
|
+
- **NIP-16** — Event treatment (regular, replaceable, ephemeral)
|
|
18
|
+
|
|
19
|
+
## Options
|
|
20
|
+
|
|
21
|
+
| Option | Description | Default |
|
|
22
|
+
|--------|-------------|---------|
|
|
23
|
+
| `--nostr` | Enable Nostr relay | false |
|
|
24
|
+
| `--nostr-path <path>` | WebSocket path | /relay |
|
|
25
|
+
| `--nostr-max-events <n>` | Max events in memory | 1000 |
|
|
26
|
+
|
|
27
|
+
## How It Works
|
|
28
|
+
|
|
29
|
+
- Events are stored in memory (up to `--nostr-max-events`)
|
|
30
|
+
- Replaceable events (kinds 0, 3, 10000-19999) replace previous events by the same pubkey
|
|
31
|
+
- Ephemeral events (kinds 20000-29999) are broadcast but not stored
|
|
32
|
+
- Parameterized replaceable events (kinds 30000-39999) use the `d` tag for deduplication
|
|
33
|
+
- Rate limiting: 60 events per socket per minute
|
|
34
|
+
|
|
35
|
+
## Client Usage
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { Relay } from 'nostr-tools';
|
|
39
|
+
|
|
40
|
+
const relay = await Relay.connect('wss://your.pod/relay');
|
|
41
|
+
|
|
42
|
+
// Subscribe
|
|
43
|
+
const sub = relay.subscribe([{ kinds: [1], limit: 10 }], {
|
|
44
|
+
onevent(event) { console.log(event); }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Publish
|
|
48
|
+
await relay.publish(signedEvent);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Nostr Authentication (NIP-98)
|
|
52
|
+
|
|
53
|
+
JSS also supports NIP-98 HTTP Auth for Solid operations. See [docs/authentication.md](authentication.md) for details on:
|
|
54
|
+
- NIP-98 Schnorr signature authentication
|
|
55
|
+
- `did:nostr` → WebID resolution
|
|
56
|
+
- Linking Nostr identity to your WebID profile
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# WebSocket Notifications
|
|
2
|
+
|
|
3
|
+
Real-time notifications for resource changes using the solid-0.1 protocol (SolidOS compatible).
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
jss start --notifications
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Discovery
|
|
10
|
+
|
|
11
|
+
Clients discover the WebSocket URL via the `Updates-Via` header:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
curl -I http://localhost:3000/alice/public/
|
|
15
|
+
# Updates-Via: ws://localhost:3000/.notifications
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Protocol
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Server: protocol solid-0.1
|
|
22
|
+
Client: sub http://localhost:3000/alice/public/data.json
|
|
23
|
+
Server: ack http://localhost:3000/alice/public/data.json
|
|
24
|
+
Server: pub http://localhost:3000/alice/public/data.json (on change)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## How It Works
|
|
28
|
+
|
|
29
|
+
1. Client connects to the WebSocket URL from `Updates-Via`
|
|
30
|
+
2. Server sends `protocol solid-0.1` greeting
|
|
31
|
+
3. Client subscribes: `sub <resource-url>`
|
|
32
|
+
4. Server acknowledges: `ack <resource-url>`
|
|
33
|
+
5. On any change (PUT, PATCH, DELETE), server broadcasts: `pub <resource-url>`
|
|
34
|
+
6. Container subscriptions also fire when child resources change
|
|
35
|
+
|
|
36
|
+
## ACL Enforcement
|
|
37
|
+
|
|
38
|
+
- Anonymous clients can subscribe to public resources
|
|
39
|
+
- Private resource subscriptions require authentication
|
|
40
|
+
- Cross-origin subscriptions are rejected
|
|
41
|
+
|
|
42
|
+
## Live Reload
|
|
43
|
+
|
|
44
|
+
For development, `--live-reload` injects a script that auto-refreshes the browser when files change on disk:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
jss start --live-reload --notifications
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
File system changes (editing files directly) also trigger WebSocket notifications.
|
package/docs/quotas.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Storage Quotas
|
|
2
|
+
|
|
3
|
+
Limit storage per pod to prevent abuse and manage resources.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
jss start --default-quota 50MB
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Managing Quotas
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Set quota for a user (overrides default)
|
|
13
|
+
jss quota set alice 100MB
|
|
14
|
+
|
|
15
|
+
# Show quota info
|
|
16
|
+
jss quota show alice
|
|
17
|
+
# alice:
|
|
18
|
+
# Used: 12.5 MB
|
|
19
|
+
# Limit: 100 MB
|
|
20
|
+
# Free: 87.5 MB
|
|
21
|
+
# Usage: 12%
|
|
22
|
+
|
|
23
|
+
# Recalculate from actual disk usage
|
|
24
|
+
jss quota reconcile alice
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## How It Works
|
|
28
|
+
|
|
29
|
+
- Quotas are tracked incrementally on PUT, POST, and DELETE operations
|
|
30
|
+
- When quota is exceeded, the server returns HTTP 507 Insufficient Storage
|
|
31
|
+
- Each pod stores its quota in `/{pod}/.quota.json`
|
|
32
|
+
- Use `reconcile` to fix quota drift from manual file changes
|
|
33
|
+
|
|
34
|
+
## Size Formats
|
|
35
|
+
|
|
36
|
+
Supported formats: `50MB`, `1GB`, `500KB`, `1TB`
|
package/package.json
CHANGED
package/src/auth/middleware.js
CHANGED
|
@@ -9,7 +9,7 @@ import { checkAccess, getRequiredMode } from '../wac/checker.js';
|
|
|
9
9
|
import { AccessMode } from '../wac/parser.js';
|
|
10
10
|
import * as storage from '../storage/filesystem.js';
|
|
11
11
|
import { getEffectiveUrlPath } from '../utils/url.js';
|
|
12
|
-
import { generateDatabrowserHtml, generateModuleDatabrowserHtml
|
|
12
|
+
import { generateDatabrowserHtml, generateModuleDatabrowserHtml } from '../mashlib/index.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Build a resource URL for WAC checking, normalizing path-based pod access
|
|
@@ -148,12 +148,9 @@ export function handleUnauthorized(request, reply, isAuthenticated, wacAllow, au
|
|
|
148
148
|
// If mashlib is enabled, serve mashlib instead of static error page
|
|
149
149
|
// Mashlib has built-in login functionality via panes.runDataBrowser()
|
|
150
150
|
if (request.mashlibEnabled) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
?
|
|
154
|
-
: request.mashlibModule
|
|
155
|
-
? generateModuleDatabrowserHtml(request.mashlibModule)
|
|
156
|
-
: generateDatabrowserHtml(request.url, request.mashlibCdn ? request.mashlibVersion : null);
|
|
151
|
+
const html = request.mashlibModule
|
|
152
|
+
? generateModuleDatabrowserHtml(request.mashlibModule)
|
|
153
|
+
: generateDatabrowserHtml(request.url, request.mashlibCdn ? request.mashlibVersion : null);
|
|
157
154
|
return reply.code(statusCode).type('text/html').send(html);
|
|
158
155
|
}
|
|
159
156
|
return reply.code(statusCode).type('text/html').send(getErrorPage(statusCode, isAuthenticated, request));
|
package/src/config.js
CHANGED
|
@@ -43,9 +43,6 @@ export const defaults = {
|
|
|
43
43
|
mashlibVersion: '2.0.0',
|
|
44
44
|
mashlibModule: false,
|
|
45
45
|
|
|
46
|
-
// SolidOS UI (modern Nextcloud-style interface)
|
|
47
|
-
solidosUi: false,
|
|
48
|
-
|
|
49
46
|
// Git HTTP backend
|
|
50
47
|
git: false,
|
|
51
48
|
|
|
@@ -137,7 +134,6 @@ const envMap = {
|
|
|
137
134
|
JSS_MASHLIB_CDN: 'mashlibCdn',
|
|
138
135
|
JSS_MASHLIB_VERSION: 'mashlibVersion',
|
|
139
136
|
JSS_MASHLIB_MODULE: 'mashlibModule',
|
|
140
|
-
JSS_SOLIDOS_UI: 'solidosUi',
|
|
141
137
|
JSS_GIT: 'git',
|
|
142
138
|
JSS_NOSTR: 'nostr',
|
|
143
139
|
JSS_NOSTR_PATH: 'nostrPath',
|
|
@@ -331,8 +327,7 @@ export function printConfig(config) {
|
|
|
331
327
|
console.log(` Notifications: ${config.notifications}`);
|
|
332
328
|
console.log(` IdP: ${config.idp ? (config.idpIssuer || 'enabled') : 'disabled'}`);
|
|
333
329
|
console.log(` Subdomains: ${config.subdomains ? (config.baseDomain || 'enabled') : 'disabled'}`);
|
|
334
|
-
console.log(` Mashlib: ${config.mashlibModule ? `module (${config.mashlibModule})` : config.mashlibCdn ? `CDN v${config.mashlibVersion}` :
|
|
335
|
-
console.log(` SolidOS UI: ${config.solidosUi ? 'enabled' : 'disabled'}`);
|
|
330
|
+
console.log(` Mashlib: ${config.mashlibModule ? `module (${config.mashlibModule})` : config.mashlibCdn ? `CDN v${config.mashlibVersion}` : 'disabled'}`);
|
|
336
331
|
if (config.pay) {
|
|
337
332
|
console.log(` Pay: ${config.payCost} sat/req`);
|
|
338
333
|
if (config.payToken) console.log(` Token: ${config.payToken} @ ${config.payRate} sat/token`);
|
package/src/handlers/resource.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from '../rdf/conneg.js';
|
|
16
16
|
import { emitChange } from '../notifications/events.js';
|
|
17
17
|
import { checkIfMatch, checkIfNoneMatchForGet, checkIfNoneMatchForWrite } from '../utils/conditional.js';
|
|
18
|
-
import { generateDatabrowserHtml, generateModuleDatabrowserHtml,
|
|
18
|
+
import { generateDatabrowserHtml, generateModuleDatabrowserHtml, shouldServeMashlib } from '../mashlib/index.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Live reload script - injected into HTML when --live-reload is enabled
|
|
@@ -230,12 +230,9 @@ export async function handleGet(request, reply) {
|
|
|
230
230
|
|
|
231
231
|
// Check if we should serve Mashlib data browser for containers
|
|
232
232
|
if (shouldServeMashlib(request, request.mashlibEnabled, 'application/ld+json')) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
?
|
|
236
|
-
: request.mashlibModule
|
|
237
|
-
? generateModuleDatabrowserHtml(request.mashlibModule)
|
|
238
|
-
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
|
|
233
|
+
const html = request.mashlibModule
|
|
234
|
+
? generateModuleDatabrowserHtml(request.mashlibModule)
|
|
235
|
+
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
|
|
239
236
|
const headers = getAllHeaders({
|
|
240
237
|
isContainer: true,
|
|
241
238
|
etag: stats.etag,
|
|
@@ -309,12 +306,9 @@ export async function handleGet(request, reply) {
|
|
|
309
306
|
// Check if we should serve Mashlib data browser
|
|
310
307
|
// Only for RDF resources when Accept: text/html is requested
|
|
311
308
|
if (shouldServeMashlib(request, request.mashlibEnabled, storedContentType)) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
?
|
|
315
|
-
: request.mashlibModule
|
|
316
|
-
? generateModuleDatabrowserHtml(request.mashlibModule)
|
|
317
|
-
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
|
|
309
|
+
const html = request.mashlibModule
|
|
310
|
+
? generateModuleDatabrowserHtml(request.mashlibModule)
|
|
311
|
+
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
|
|
318
312
|
const headers = getAllHeaders({
|
|
319
313
|
isContainer: false,
|
|
320
314
|
etag: stats.etag,
|
package/src/server.js
CHANGED
|
@@ -62,14 +62,11 @@ export function createServer(options = {}) {
|
|
|
62
62
|
const subdomainsEnabled = options.subdomains ?? false;
|
|
63
63
|
const baseDomain = options.baseDomain || null;
|
|
64
64
|
// Mashlib data browser is OFF by default
|
|
65
|
-
// mashlibCdn:
|
|
66
|
-
// mashlibModule: URL to ES module entry point (alternative to classic mashlib)
|
|
65
|
+
// mashlibCdn: load from CDN; mashlibModule: URL to ES module entry point
|
|
67
66
|
const mashlibModule = options.mashlibModule ?? false;
|
|
68
|
-
const mashlibEnabled = options.mashlib || !!mashlibModule;
|
|
69
67
|
const mashlibCdn = options.mashlibCdn ?? false;
|
|
68
|
+
const mashlibEnabled = mashlibCdn || !!mashlibModule;
|
|
70
69
|
const mashlibVersion = options.mashlibVersion ?? '2.0.0';
|
|
71
|
-
// SolidOS UI (modern Nextcloud-style interface) - requires mashlib
|
|
72
|
-
const solidosUiEnabled = options.solidosUi ?? false;
|
|
73
70
|
// Git HTTP backend is OFF by default - enables clone/push via git protocol
|
|
74
71
|
const gitEnabled = options.git ?? false;
|
|
75
72
|
// Nostr relay is OFF by default
|
|
@@ -179,7 +176,6 @@ export function createServer(options = {}) {
|
|
|
179
176
|
fastify.decorateRequest('mashlibCdn', null);
|
|
180
177
|
fastify.decorateRequest('mashlibVersion', null);
|
|
181
178
|
fastify.decorateRequest('mashlibModule', null);
|
|
182
|
-
fastify.decorateRequest('solidosUiEnabled', null);
|
|
183
179
|
fastify.decorateRequest('defaultQuota', null);
|
|
184
180
|
fastify.decorateRequest('config', null);
|
|
185
181
|
fastify.decorateRequest('liveReloadEnabled', null);
|
|
@@ -193,7 +189,6 @@ export function createServer(options = {}) {
|
|
|
193
189
|
request.mashlibCdn = mashlibCdn;
|
|
194
190
|
request.mashlibVersion = mashlibVersion;
|
|
195
191
|
request.mashlibModule = mashlibModule;
|
|
196
|
-
request.solidosUiEnabled = solidosUiEnabled;
|
|
197
192
|
request.defaultQuota = defaultQuota;
|
|
198
193
|
request.config = { public: options.public, readOnly: options.readOnly };
|
|
199
194
|
request.liveReloadEnabled = liveReloadEnabled;
|
|
@@ -410,7 +405,7 @@ export function createServer(options = {}) {
|
|
|
410
405
|
// Authorization hook - check WAC permissions
|
|
411
406
|
// Skip for pod creation endpoint (needs special handling)
|
|
412
407
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
413
|
-
// Skip auth for pod creation, OPTIONS, IdP routes, mashlib,
|
|
408
|
+
// Skip auth for pod creation, OPTIONS, IdP routes, mashlib, well-known, notifications, nostr, git, and AP
|
|
414
409
|
const mashlibPaths = ['/mashlib.min.js', '/mash.css', '/841.mashlib.min.js'];
|
|
415
410
|
const apPaths = ['/inbox', '/profile/card/inbox', '/profile/card/outbox', '/profile/card/followers', '/profile/card/following',
|
|
416
411
|
'/api/v1/apps', '/api/v1/instance', '/api/v1/accounts/verify_credentials',
|
|
@@ -424,7 +419,6 @@ export function createServer(options = {}) {
|
|
|
424
419
|
request.method === 'OPTIONS' ||
|
|
425
420
|
request.url.startsWith('/idp/') ||
|
|
426
421
|
request.url.startsWith('/.well-known/') ||
|
|
427
|
-
request.url.startsWith('/solidos-ui/') ||
|
|
428
422
|
(nostrEnabled && request.url.startsWith(nostrPath)) ||
|
|
429
423
|
(gitEnabled && isGitRequest(request.url)) ||
|
|
430
424
|
(activitypubEnabled && apPaths.some(p => request.url === p || request.url.startsWith(p + '?'))) ||
|
|
@@ -464,72 +458,15 @@ export function createServer(options = {}) {
|
|
|
464
458
|
}
|
|
465
459
|
}, handleCreatePod);
|
|
466
460
|
|
|
467
|
-
// Mashlib
|
|
468
|
-
if (mashlibEnabled) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
// Mashlib uses code splitting, so it loads chunks like 789.mashlib.min.js
|
|
472
|
-
const cdnBase = `https://unpkg.com/mashlib@${mashlibVersion}/dist`;
|
|
473
|
-
const chunkPattern = /^\/\d+\.mashlib\.min\.js(\.map)?$/;
|
|
474
|
-
|
|
475
|
-
fastify.addHook('onRequest', async (request, reply) => {
|
|
476
|
-
if (chunkPattern.test(request.url)) {
|
|
477
|
-
const filename = request.url.split('/').pop();
|
|
478
|
-
return reply.redirect(302, `${cdnBase}/${filename}`);
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
} else {
|
|
482
|
-
// Local mode: serve from local files
|
|
483
|
-
const mashlibDir = join(__dirname, 'mashlib-local', 'dist');
|
|
484
|
-
const mashlibFiles = {
|
|
485
|
-
'/mashlib.min.js': { file: 'mashlib.min.js', type: 'application/javascript' },
|
|
486
|
-
'/mashlib.min.js.map': { file: 'mashlib.min.js.map', type: 'application/json' },
|
|
487
|
-
'/mash.css': { file: 'mash.css', type: 'text/css' },
|
|
488
|
-
'/mash.css.map': { file: 'mash.css.map', type: 'application/json' },
|
|
489
|
-
'/841.mashlib.min.js': { file: '841.mashlib.min.js', type: 'application/javascript' },
|
|
490
|
-
'/841.mashlib.min.js.map': { file: '841.mashlib.min.js.map', type: 'application/json' }
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
for (const [path, config] of Object.entries(mashlibFiles)) {
|
|
494
|
-
fastify.get(path, async (request, reply) => {
|
|
495
|
-
try {
|
|
496
|
-
const content = await readFile(join(mashlibDir, config.file));
|
|
497
|
-
return reply.type(config.type).send(content);
|
|
498
|
-
} catch {
|
|
499
|
-
return reply.code(404).send({ error: 'Not Found' });
|
|
500
|
-
}
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
461
|
+
// Mashlib CDN mode: redirect chunk requests to CDN
|
|
462
|
+
if (mashlibEnabled && mashlibCdn) {
|
|
463
|
+
const cdnBase = `https://unpkg.com/mashlib@${mashlibVersion}/dist`;
|
|
464
|
+
const chunkPattern = /^\/\d+\.mashlib\.min\.js(\.map)?$/;
|
|
505
465
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
// Serve all files under /solidos-ui/* path
|
|
512
|
-
fastify.get('/solidos-ui/*', async (request, reply) => {
|
|
513
|
-
try {
|
|
514
|
-
// Get the path after /solidos-ui/
|
|
515
|
-
const filePath = request.url.replace('/solidos-ui/', '').split('?')[0];
|
|
516
|
-
const fullPath = join(solidosUiDir, filePath);
|
|
517
|
-
|
|
518
|
-
// Determine content type based on extension
|
|
519
|
-
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
520
|
-
const contentTypes = {
|
|
521
|
-
'js': 'application/javascript',
|
|
522
|
-
'css': 'text/css',
|
|
523
|
-
'map': 'application/json',
|
|
524
|
-
'html': 'text/html'
|
|
525
|
-
};
|
|
526
|
-
const contentType = contentTypes[ext] || 'application/octet-stream';
|
|
527
|
-
|
|
528
|
-
const content = await readFile(fullPath);
|
|
529
|
-
return reply.type(contentType).send(content);
|
|
530
|
-
} catch (err) {
|
|
531
|
-
request.log.error(err, 'Failed to serve solidos-ui file');
|
|
532
|
-
return reply.code(404).send({ error: 'Not Found' });
|
|
466
|
+
fastify.addHook('onRequest', async (request, reply) => {
|
|
467
|
+
if (chunkPattern.test(request.url)) {
|
|
468
|
+
const filename = request.url.split('/').pop();
|
|
469
|
+
return reply.redirect(302, `${cdnBase}/${filename}`);
|
|
533
470
|
}
|
|
534
471
|
});
|
|
535
472
|
}
|