Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions bin/jss.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,10 @@ program
.option('--subdomains', 'Enable subdomain-based pods (XSS protection)')
.option('--no-subdomains', 'Disable subdomain-based pods')
.option('--base-domain <domain>', 'Base domain for subdomain pods (e.g., "example.com")')
.option('--mashlib', 'Enable Mashlib data browser (local mode, requires mashlib in node_modules)')
.option('--mashlib-cdn', 'Enable Mashlib data browser (CDN mode, no local files needed)')
.option('--mashlib-cdn', 'Enable Mashlib data browser (CDN mode)')
.option('--mashlib-module <url>', 'Enable ES module data browser from a URL')
.option('--no-mashlib', 'Disable Mashlib data browser')
.option('--mashlib-version <version>', 'Mashlib version for CDN mode (default: 2.0.0)')
.option('--solidos-ui', 'Enable modern Nextcloud-style UI (requires --mashlib)')
.option('--git', 'Enable Git HTTP backend (clone/push support)')
.option('--no-git', 'Disable Git HTTP backend')
.option('--nostr', 'Enable Nostr relay')
Expand Down Expand Up @@ -143,7 +141,6 @@ program
mashlibCdn: config.mashlibCdn,
mashlibVersion: config.mashlibVersion,
mashlibModule: config.mashlibModule,
solidosUi: config.solidosUi,
git: config.git,
nostr: config.nostr,
nostrPath: config.nostrPath,
Expand Down Expand Up @@ -193,7 +190,6 @@ program
console.log(` Mashlib: local (data browser enabled)`);
}
if (config.mashlibModule) console.log(` Mashlib module: ${config.mashlibModule}`);
if (config.solidosUi) console.log(' SolidOS UI: enabled (modern interface)');
if (config.git) console.log(' Git: enabled (clone/push support)');
if (config.nostr) console.log(` Nostr: enabled (${config.nostrPath})`);
if (config.webrtc) console.log(` WebRTC: enabled (${config.webrtcPath || '/.webrtc'})`);
Expand Down
11 changes: 4 additions & 7 deletions src/auth/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { checkAccess, getRequiredMode } from '../wac/checker.js';
import { AccessMode } from '../wac/parser.js';
import * as storage from '../storage/filesystem.js';
import { getEffectiveUrlPath } from '../utils/url.js';
import { generateDatabrowserHtml, generateModuleDatabrowserHtml, generateSolidosUiHtml } from '../mashlib/index.js';
import { generateDatabrowserHtml, generateModuleDatabrowserHtml } from '../mashlib/index.js';

/**
* Build a resource URL for WAC checking, normalizing path-based pod access
Expand Down Expand Up @@ -148,12 +148,9 @@ export function handleUnauthorized(request, reply, isAuthenticated, wacAllow, au
// If mashlib is enabled, serve mashlib instead of static error page
// Mashlib has built-in login functionality via panes.runDataBrowser()
if (request.mashlibEnabled) {
// Use SolidOS UI if enabled, ES module if configured, otherwise classic mashlib
const html = request.solidosUiEnabled
? generateSolidosUiHtml()
: request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(request.url, request.mashlibCdn ? request.mashlibVersion : null);
const html = request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(request.url, request.mashlibCdn ? request.mashlibVersion : null);
return reply.code(statusCode).type('text/html').send(html);
}
return reply.code(statusCode).type('text/html').send(getErrorPage(statusCode, isAuthenticated, request));
Expand Down
7 changes: 1 addition & 6 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ export const defaults = {
mashlibVersion: '2.0.0',
mashlibModule: false,

// SolidOS UI (modern Nextcloud-style interface)
solidosUi: false,

// Git HTTP backend
git: false,

Expand Down Expand Up @@ -137,7 +134,6 @@ const envMap = {
JSS_MASHLIB_CDN: 'mashlibCdn',
JSS_MASHLIB_VERSION: 'mashlibVersion',
JSS_MASHLIB_MODULE: 'mashlibModule',
JSS_SOLIDOS_UI: 'solidosUi',
JSS_GIT: 'git',
JSS_NOSTR: 'nostr',
JSS_NOSTR_PATH: 'nostrPath',
Expand Down Expand Up @@ -331,8 +327,7 @@ export function printConfig(config) {
console.log(` Notifications: ${config.notifications}`);
console.log(` IdP: ${config.idp ? (config.idpIssuer || 'enabled') : 'disabled'}`);
console.log(` Subdomains: ${config.subdomains ? (config.baseDomain || 'enabled') : 'disabled'}`);
console.log(` Mashlib: ${config.mashlibModule ? `module (${config.mashlibModule})` : config.mashlibCdn ? `CDN v${config.mashlibVersion}` : config.mashlib ? 'local' : 'disabled'}`);
console.log(` SolidOS UI: ${config.solidosUi ? 'enabled' : 'disabled'}`);
console.log(` Mashlib: ${config.mashlibModule ? `module (${config.mashlibModule})` : config.mashlibCdn ? `CDN v${config.mashlibVersion}` : 'disabled'}`);
if (config.pay) {
console.log(` Pay: ${config.payCost} sat/req`);
if (config.payToken) console.log(` Token: ${config.payToken} @ ${config.payRate} sat/token`);
Expand Down
20 changes: 7 additions & 13 deletions src/handlers/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from '../rdf/conneg.js';
import { emitChange } from '../notifications/events.js';
import { checkIfMatch, checkIfNoneMatchForGet, checkIfNoneMatchForWrite } from '../utils/conditional.js';
import { generateDatabrowserHtml, generateModuleDatabrowserHtml, generateSolidosUiHtml, shouldServeMashlib } from '../mashlib/index.js';
import { generateDatabrowserHtml, generateModuleDatabrowserHtml, shouldServeMashlib } from '../mashlib/index.js';

/**
* Live reload script - injected into HTML when --live-reload is enabled
Expand Down Expand Up @@ -230,12 +230,9 @@ export async function handleGet(request, reply) {

// Check if we should serve Mashlib data browser for containers
if (shouldServeMashlib(request, request.mashlibEnabled, 'application/ld+json')) {
// Use SolidOS UI if enabled, ES module if configured, otherwise classic mashlib
const html = request.solidosUiEnabled
? generateSolidosUiHtml()
: request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
const html = request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
const headers = getAllHeaders({
isContainer: true,
etag: stats.etag,
Expand Down Expand Up @@ -309,12 +306,9 @@ export async function handleGet(request, reply) {
// Check if we should serve Mashlib data browser
// Only for RDF resources when Accept: text/html is requested
if (shouldServeMashlib(request, request.mashlibEnabled, storedContentType)) {
// Use SolidOS UI if enabled, ES module if configured, otherwise classic mashlib
const html = request.solidosUiEnabled
? generateSolidosUiHtml()
: request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
const html = request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
const headers = getAllHeaders({
isContainer: false,
etag: stats.etag,
Expand Down
85 changes: 11 additions & 74 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,11 @@ export function createServer(options = {}) {
const subdomainsEnabled = options.subdomains ?? false;
const baseDomain = options.baseDomain || null;
// Mashlib data browser is OFF by default
// mashlibCdn: if true, load from CDN; if false, serve locally
// mashlibModule: URL to ES module entry point (alternative to classic mashlib)
// mashlibCdn: load from CDN; mashlibModule: URL to ES module entry point
const mashlibModule = options.mashlibModule ?? false;
const mashlibEnabled = options.mashlib || !!mashlibModule;
const mashlibCdn = options.mashlibCdn ?? false;
const mashlibEnabled = mashlibCdn || !!mashlibModule;
const mashlibVersion = options.mashlibVersion ?? '2.0.0';
// SolidOS UI (modern Nextcloud-style interface) - requires mashlib
const solidosUiEnabled = options.solidosUi ?? false;
// Git HTTP backend is OFF by default - enables clone/push via git protocol
const gitEnabled = options.git ?? false;
// Nostr relay is OFF by default
Expand Down Expand Up @@ -179,7 +176,6 @@ export function createServer(options = {}) {
fastify.decorateRequest('mashlibCdn', null);
fastify.decorateRequest('mashlibVersion', null);
fastify.decorateRequest('mashlibModule', null);
fastify.decorateRequest('solidosUiEnabled', null);
fastify.decorateRequest('defaultQuota', null);
fastify.decorateRequest('config', null);
fastify.decorateRequest('liveReloadEnabled', null);
Expand All @@ -193,7 +189,6 @@ export function createServer(options = {}) {
request.mashlibCdn = mashlibCdn;
request.mashlibVersion = mashlibVersion;
request.mashlibModule = mashlibModule;
request.solidosUiEnabled = solidosUiEnabled;
request.defaultQuota = defaultQuota;
request.config = { public: options.public, readOnly: options.readOnly };
request.liveReloadEnabled = liveReloadEnabled;
Expand Down Expand Up @@ -410,7 +405,7 @@ export function createServer(options = {}) {
// Authorization hook - check WAC permissions
// Skip for pod creation endpoint (needs special handling)
fastify.addHook('preHandler', async (request, reply) => {
// Skip auth for pod creation, OPTIONS, IdP routes, mashlib, solidos-ui, well-known, notifications, nostr, git, and AP
// Skip auth for pod creation, OPTIONS, IdP routes, mashlib, well-known, notifications, nostr, git, and AP
const mashlibPaths = ['/mashlib.min.js', '/mash.css', '/841.mashlib.min.js'];
const apPaths = ['/inbox', '/profile/card/inbox', '/profile/card/outbox', '/profile/card/followers', '/profile/card/following',
'/api/v1/apps', '/api/v1/instance', '/api/v1/accounts/verify_credentials',
Expand All @@ -424,7 +419,6 @@ export function createServer(options = {}) {
request.method === 'OPTIONS' ||
request.url.startsWith('/idp/') ||
request.url.startsWith('/.well-known/') ||
request.url.startsWith('/solidos-ui/') ||
(nostrEnabled && request.url.startsWith(nostrPath)) ||
(gitEnabled && isGitRequest(request.url)) ||
(activitypubEnabled && apPaths.some(p => request.url === p || request.url.startsWith(p + '?'))) ||
Expand Down Expand Up @@ -464,72 +458,15 @@ export function createServer(options = {}) {
}
}, handleCreatePod);

// Mashlib static files (served from root like NSS does)
if (mashlibEnabled) {
if (mashlibCdn) {
// CDN mode: redirect chunk requests to CDN
// Mashlib uses code splitting, so it loads chunks like 789.mashlib.min.js
const cdnBase = `https://unpkg.com/mashlib@${mashlibVersion}/dist`;
const chunkPattern = /^\/\d+\.mashlib\.min\.js(\.map)?$/;

fastify.addHook('onRequest', async (request, reply) => {
if (chunkPattern.test(request.url)) {
const filename = request.url.split('/').pop();
return reply.redirect(302, `${cdnBase}/${filename}`);
}
});
} else {
// Local mode: serve from local files
const mashlibDir = join(__dirname, 'mashlib-local', 'dist');
const mashlibFiles = {
'/mashlib.min.js': { file: 'mashlib.min.js', type: 'application/javascript' },
'/mashlib.min.js.map': { file: 'mashlib.min.js.map', type: 'application/json' },
'/mash.css': { file: 'mash.css', type: 'text/css' },
'/mash.css.map': { file: 'mash.css.map', type: 'application/json' },
'/841.mashlib.min.js': { file: '841.mashlib.min.js', type: 'application/javascript' },
'/841.mashlib.min.js.map': { file: '841.mashlib.min.js.map', type: 'application/json' }
};

for (const [path, config] of Object.entries(mashlibFiles)) {
fastify.get(path, async (request, reply) => {
try {
const content = await readFile(join(mashlibDir, config.file));
return reply.type(config.type).send(content);
} catch {
return reply.code(404).send({ error: 'Not Found' });
}
});
}
}
}
// Mashlib CDN mode: redirect chunk requests to CDN
if (mashlibEnabled && mashlibCdn) {
const cdnBase = `https://unpkg.com/mashlib@${mashlibVersion}/dist`;
const chunkPattern = /^\/\d+\.mashlib\.min\.js(\.map)?$/;

// SolidOS UI static files (modern Nextcloud-style interface)
// Serves from /solidos-ui/* - requires mashlib to be enabled as well
if (solidosUiEnabled && mashlibEnabled) {
const solidosUiDir = join(__dirname, 'mashlib-local', 'dist', 'solidos-ui');

// Serve all files under /solidos-ui/* path
fastify.get('/solidos-ui/*', async (request, reply) => {
try {
// Get the path after /solidos-ui/
const filePath = request.url.replace('/solidos-ui/', '').split('?')[0];
const fullPath = join(solidosUiDir, filePath);

// Determine content type based on extension
const ext = filePath.split('.').pop()?.toLowerCase();
const contentTypes = {
'js': 'application/javascript',
'css': 'text/css',
'map': 'application/json',
'html': 'text/html'
};
const contentType = contentTypes[ext] || 'application/octet-stream';

const content = await readFile(fullPath);
return reply.type(contentType).send(content);
} catch (err) {
request.log.error(err, 'Failed to serve solidos-ui file');
return reply.code(404).send({ error: 'Not Found' });
fastify.addHook('onRequest', async (request, reply) => {
if (chunkPattern.test(request.url)) {
const filename = request.url.split('/').pop();
return reply.redirect(302, `${cdnBase}/${filename}`);
}
});
}
Expand Down