2022-06-18 13:39:08 -04:00
|
|
|
import * as auth from './auth.js';
|
|
|
|
import * as app from './app.js';
|
|
|
|
import * as httpd from './httpd.js';
|
2016-03-12 13:50:43 -05:00
|
|
|
|
2023-01-28 17:44:45 -05:00
|
|
|
let gProcessIndex = 0;
|
|
|
|
let gProcesses = {};
|
|
|
|
let gStatsTimer = false;
|
2016-03-12 13:50:43 -05:00
|
|
|
|
2022-11-30 19:26:51 -05:00
|
|
|
const k_global_settings = {
|
|
|
|
index: {
|
|
|
|
type: 'string',
|
|
|
|
default_value: '/~core/apps/',
|
|
|
|
description: 'Default path.',
|
|
|
|
},
|
|
|
|
room: {
|
|
|
|
type: 'boolean',
|
|
|
|
default_value: true,
|
|
|
|
description: 'Whether this instance should behave as a room.',
|
|
|
|
},
|
2023-01-07 19:25:38 -05:00
|
|
|
room_name: {
|
|
|
|
type: 'string',
|
|
|
|
default_value: 'tilde friends tunnel',
|
|
|
|
description: 'Name of the room.',
|
|
|
|
},
|
2022-12-24 14:25:21 -05:00
|
|
|
code_of_conduct: {
|
|
|
|
type: 'textarea',
|
|
|
|
default_value: undefined,
|
|
|
|
description: 'Code of conduct presented at sign-in.',
|
|
|
|
},
|
2023-01-11 18:39:42 -05:00
|
|
|
http_redirect: {
|
|
|
|
type: 'string',
|
|
|
|
default_value: undefined,
|
|
|
|
description: 'If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "https://example.com")',
|
|
|
|
},
|
2022-11-30 19:26:51 -05:00
|
|
|
};
|
|
|
|
|
2023-01-28 17:44:45 -05:00
|
|
|
let gGlobalSettings = {
|
2022-10-12 08:27:32 -04:00
|
|
|
index: "/~core/apps/",
|
2016-03-12 13:50:43 -05:00
|
|
|
};
|
|
|
|
|
2023-01-28 17:44:45 -05:00
|
|
|
let kPingInterval = 60 * 1000;
|
2016-03-12 13:50:43 -05:00
|
|
|
|
|
|
|
function printError(out, error) {
|
|
|
|
if (error.stackTrace) {
|
|
|
|
out.print(error.fileName + ":" + error.lineNumber + ": " + error.message);
|
|
|
|
out.print(error.stackTrace);
|
|
|
|
} else {
|
2023-01-28 17:44:45 -05:00
|
|
|
for (let i in error) {
|
2016-03-12 13:50:43 -05:00
|
|
|
out.print(i);
|
|
|
|
}
|
|
|
|
out.print(error.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-02 13:10:00 -05:00
|
|
|
function invoke(handlers, argv) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let promises = [];
|
2021-01-02 13:10:00 -05:00
|
|
|
if (handlers) {
|
2023-01-28 17:44:45 -05:00
|
|
|
for (let i = 0; i < handlers.length; ++i) {
|
2021-01-02 13:10:00 -05:00
|
|
|
try {
|
|
|
|
promises.push(handlers[i](...argv));
|
|
|
|
} catch (error) {
|
|
|
|
handlers.splice(i, 1);
|
|
|
|
i--;
|
|
|
|
promises.push(new Promise(function(resolve, reject) { reject(error); }));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
2016-03-12 13:50:43 -05:00
|
|
|
function broadcastEvent(eventName, argv) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let promises = [];
|
|
|
|
for (let i in gProcesses) {
|
|
|
|
let process = gProcesses[i];
|
2021-01-02 13:10:00 -05:00
|
|
|
if (process.eventHandlers[eventName]) {
|
|
|
|
promises.push(invoke(process.eventHandlers[eventName], argv));
|
|
|
|
}
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
function broadcast(message) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let sender = this;
|
|
|
|
let promises = [];
|
|
|
|
for (let i in gProcesses) {
|
|
|
|
let process = gProcesses[i];
|
2016-03-12 13:50:43 -05:00
|
|
|
if (process != sender
|
|
|
|
&& process.packageOwner == sender.packageOwner
|
|
|
|
&& process.packageName == sender.packageName) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let from = getUser(process, sender);
|
2016-03-12 13:50:43 -05:00
|
|
|
promises.push(postMessageInternal(from, process, message));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getUser(caller, process) {
|
|
|
|
return {
|
|
|
|
name: process.userName,
|
2016-03-12 13:55:55 -05:00
|
|
|
key: process.key,
|
2016-03-12 13:50:43 -05:00
|
|
|
index: process.index,
|
|
|
|
packageOwner: process.packageOwner,
|
|
|
|
packageName: process.packageName,
|
|
|
|
credentials: process.credentials,
|
|
|
|
postMessage: postMessageInternal.bind(caller, caller, process),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-02-13 17:03:12 -05:00
|
|
|
function getApps(user, process) {
|
|
|
|
if (process.credentials &&
|
|
|
|
process.credentials.session &&
|
|
|
|
process.credentials.session.name) {
|
|
|
|
if (user && user !== process.credentials.session.name && user !== 'core') {
|
|
|
|
return {};
|
|
|
|
} else if (!user) {
|
|
|
|
user = process.credentials.session.name;
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
}
|
2022-02-13 17:03:12 -05:00
|
|
|
if (user) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let db = new Database(user);
|
2022-02-13 17:03:12 -05:00
|
|
|
try {
|
2023-01-28 17:44:45 -05:00
|
|
|
let names = JSON.parse(db.get('apps'));
|
2022-02-13 17:03:12 -05:00
|
|
|
return Object.fromEntries(names.map(name => [name, db.get('path:' + name)]));
|
|
|
|
} catch {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function postMessageInternal(from, to, message) {
|
2021-01-02 13:10:00 -05:00
|
|
|
if (to.eventHandlers['message']) {
|
|
|
|
return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
|
|
|
|
}
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
|
2021-01-02 13:10:00 -05:00
|
|
|
async function getSessionProcessBlob(blobId, session, options) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let actualOptions = {timeout: kPingInterval};
|
2016-03-12 13:50:43 -05:00
|
|
|
if (options) {
|
2023-01-28 17:44:45 -05:00
|
|
|
for (let i in options) {
|
2016-03-12 13:50:43 -05:00
|
|
|
actualOptions[i] = options[i];
|
|
|
|
}
|
|
|
|
}
|
2021-01-02 13:10:00 -05:00
|
|
|
return getProcessBlob(blobId, 'session_' + session, actualOptions);
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
|
2017-01-12 20:04:00 -05:00
|
|
|
let gManifestCache = {};
|
|
|
|
|
2021-01-02 13:10:00 -05:00
|
|
|
async function getProcessBlob(blobId, key, options) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let process = gProcesses[key];
|
2016-03-12 13:50:43 -05:00
|
|
|
if (!process
|
2021-01-02 13:10:00 -05:00
|
|
|
&& !(options && "create" in options && !options.create)) {
|
2016-03-12 13:50:43 -05:00
|
|
|
try {
|
2021-01-02 13:10:00 -05:00
|
|
|
print("Creating task for " + blobId + " " + key);
|
2016-03-12 13:50:43 -05:00
|
|
|
process = {};
|
|
|
|
process.key = key;
|
|
|
|
process.index = gProcessIndex++;
|
2021-01-02 13:10:00 -05:00
|
|
|
process.userName = 'user' + process.index;
|
2016-03-12 13:50:43 -05:00
|
|
|
process.credentials = options.credentials || {};
|
|
|
|
process.task = new Task();
|
|
|
|
process.eventHandlers = {};
|
2022-03-17 21:24:29 -04:00
|
|
|
process.app = new app.App();
|
2016-03-12 13:50:43 -05:00
|
|
|
process.lastActive = Date.now();
|
|
|
|
process.lastPing = null;
|
|
|
|
process.timeout = options.timeout;
|
2022-02-26 16:17:15 -05:00
|
|
|
process.stats = false;
|
2023-01-28 17:44:45 -05:00
|
|
|
let resolveReady;
|
|
|
|
let rejectReady;
|
2016-03-12 13:50:43 -05:00
|
|
|
process.ready = new Promise(function(resolve, reject) {
|
|
|
|
resolveReady = resolve;
|
|
|
|
rejectReady = reject;
|
|
|
|
});
|
|
|
|
gProcesses[key] = process;
|
|
|
|
process.task.onExit = function(exitCode, terminationSignal) {
|
|
|
|
broadcastEvent('onSessionEnd', [getUser(process, process)]);
|
2021-01-02 13:10:00 -05:00
|
|
|
process.task = null;
|
2016-03-12 13:50:43 -05:00
|
|
|
delete gProcesses[key];
|
|
|
|
};
|
2023-01-28 17:44:45 -05:00
|
|
|
let imports = {
|
2016-03-12 13:50:43 -05:00
|
|
|
'core': {
|
|
|
|
'broadcast': broadcast.bind(process),
|
|
|
|
'register': function(eventName, handler) {
|
|
|
|
if (!process.eventHandlers[eventName]) {
|
|
|
|
process.eventHandlers[eventName] = [];
|
|
|
|
}
|
|
|
|
process.eventHandlers[eventName].push(handler);
|
|
|
|
},
|
2022-03-27 15:53:02 -04:00
|
|
|
'unregister': function(eventName, handler) {
|
2021-01-02 13:10:00 -05:00
|
|
|
if (process.eventHandlers[eventName]) {
|
2016-04-03 15:31:03 -04:00
|
|
|
let index = process.eventHandlers[eventName].indexOf(handler);
|
|
|
|
if (index != -1) {
|
|
|
|
process.eventHandlers[eventName].splice(index, 1);
|
|
|
|
}
|
|
|
|
if (process.eventHandlers[eventName].length == 0) {
|
|
|
|
delete process.eventHandlers[eventName];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2016-03-12 13:50:43 -05:00
|
|
|
'user': getUser(process, process),
|
2022-08-03 20:07:12 -04:00
|
|
|
'users': function() {
|
|
|
|
try {
|
|
|
|
return JSON.parse(new Database('auth').get('users'));
|
|
|
|
} catch {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
},
|
2022-08-13 21:46:11 -04:00
|
|
|
'permissionsGranted': function() {
|
|
|
|
let user = process?.credentials?.session?.name;
|
|
|
|
if (user &&
|
|
|
|
options?.packageOwner &&
|
|
|
|
options?.packageName &&
|
|
|
|
gGlobalSettings.userPermissions &&
|
|
|
|
gGlobalSettings.userPermissions[user] &&
|
|
|
|
gGlobalSettings.userPermissions[user][options.packageOwner]) {
|
|
|
|
return gGlobalSettings.userPermissions[user][options.packageOwner][options.packageName];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'allPermissionsGranted': function() {
|
|
|
|
let user = process?.credentials?.session?.name;
|
|
|
|
if (user &&
|
|
|
|
options?.packageOwner &&
|
|
|
|
options?.packageName &&
|
|
|
|
gGlobalSettings.userPermissions &&
|
|
|
|
gGlobalSettings.userPermissions[user]) {
|
|
|
|
return gGlobalSettings.userPermissions[user];
|
|
|
|
}
|
|
|
|
},
|
2022-08-13 14:06:30 -04:00
|
|
|
'permissionsForUser': function(user) {
|
|
|
|
return (gGlobalSettings?.permissions ? gGlobalSettings.permissions[user] : []) ?? [];
|
|
|
|
},
|
2022-02-13 17:03:12 -05:00
|
|
|
'apps': user => getApps(user, process),
|
2022-06-08 22:45:34 -04:00
|
|
|
'getSockets': getSockets,
|
2022-07-26 20:27:10 -04:00
|
|
|
'permissionTest': function(permission) {
|
|
|
|
let user = process?.credentials?.session?.name;
|
|
|
|
if (!user || !options?.packageOwner || !options?.packageName) {
|
2022-08-13 21:46:11 -04:00
|
|
|
return;
|
2022-07-26 20:27:10 -04:00
|
|
|
} else if (gGlobalSettings.userPermissions &&
|
|
|
|
gGlobalSettings.userPermissions[user] &&
|
|
|
|
gGlobalSettings.userPermissions[user][options.packageOwner] &&
|
|
|
|
gGlobalSettings.userPermissions[user][options.packageOwner][options.packageName] &&
|
|
|
|
gGlobalSettings.userPermissions[user][options.packageOwner][options.packageName][permission] !== undefined) {
|
|
|
|
if (gGlobalSettings.userPermissions[user][options.packageOwner][options.packageName][permission]) {
|
2022-08-13 21:46:11 -04:00
|
|
|
return true;
|
2022-07-26 20:27:10 -04:00
|
|
|
} else {
|
2022-08-14 13:34:27 -04:00
|
|
|
throw Error(`Permission denied: ${permission}.`);
|
2022-07-26 20:27:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2022-08-13 21:46:11 -04:00
|
|
|
return process.app.makeFunction(['requestPermission'])(permission).then(function(value) {
|
2022-07-26 20:27:10 -04:00
|
|
|
if (value == 'allow') {
|
|
|
|
storePermission(user, options.packageOwner, options.packageName, permission, true);
|
2022-08-14 14:24:41 -04:00
|
|
|
process.sendPermissions();
|
2022-07-26 20:27:10 -04:00
|
|
|
return true;
|
|
|
|
} else if (value == 'allow once') {
|
|
|
|
return true;
|
2022-08-13 21:46:11 -04:00
|
|
|
} else if (value == 'deny') {
|
2022-07-26 20:27:10 -04:00
|
|
|
storePermission(user, options.packageOwner, options.packageName, permission, false);
|
2022-08-14 14:24:41 -04:00
|
|
|
process.sendPermissions();
|
2022-08-14 13:34:27 -04:00
|
|
|
throw Error(`Permission denied: ${permission}.`);
|
2022-07-26 20:27:10 -04:00
|
|
|
} else if (value == 'deny once') {
|
2022-08-14 13:34:27 -04:00
|
|
|
throw Error(`Permission denied: ${permission}.`);
|
2022-07-26 20:27:10 -04:00
|
|
|
}
|
2022-08-14 13:34:27 -04:00
|
|
|
throw Error(`Permission denied: ${permission}.`);
|
2022-07-26 20:27:10 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2016-04-30 06:50:43 -04:00
|
|
|
}
|
2021-01-02 13:10:00 -05:00
|
|
|
};
|
2022-08-03 20:57:56 -04:00
|
|
|
if (process.credentials?.permissions?.administration) {
|
2022-11-30 19:26:51 -05:00
|
|
|
imports.core.globalSettingsDescriptions = function() {
|
|
|
|
let settings = Object.assign({}, k_global_settings);
|
|
|
|
for (let [key, value] of Object.entries(gGlobalSettings)) {
|
|
|
|
if (settings[key]) {
|
|
|
|
settings[key].value = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return settings;
|
|
|
|
};
|
2022-08-16 20:29:57 -04:00
|
|
|
imports.core.globalSettingsGet = function(key) {
|
|
|
|
return gGlobalSettings[key];
|
|
|
|
};
|
|
|
|
imports.core.globalSettingsSet = function(key, value) {
|
|
|
|
print('Setting', key, value);
|
|
|
|
gGlobalSettings[key] = value;
|
|
|
|
setGlobalSettings(gGlobalSettings);
|
|
|
|
print('Done.');
|
|
|
|
};
|
2022-08-03 20:57:56 -04:00
|
|
|
imports.core.deleteUser = function(user) {
|
|
|
|
return imports.core.permissionTest('delete_user').then(function() {
|
|
|
|
let db = new Database('auth');
|
|
|
|
|
|
|
|
db.remove('user:' + user);
|
|
|
|
|
|
|
|
let users = new Set();
|
|
|
|
let users_original = db.get('users');
|
|
|
|
try {
|
|
|
|
users = new Set(JSON.parse(users_original));
|
|
|
|
} catch {
|
|
|
|
}
|
|
|
|
users.delete(user);
|
|
|
|
users = JSON.stringify([...users].sort());
|
|
|
|
if (users !== users_original) {
|
|
|
|
db.set('users', users);
|
|
|
|
}
|
|
|
|
});
|
2022-08-16 20:29:57 -04:00
|
|
|
};
|
2022-08-03 20:57:56 -04:00
|
|
|
}
|
2021-01-02 13:10:00 -05:00
|
|
|
if (options.api) {
|
|
|
|
imports.app = {};
|
|
|
|
for (let i in options.api) {
|
|
|
|
let api = options.api[i];
|
|
|
|
imports.app[api[0]] = process.app.makeFunction(api);
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
}
|
2022-02-13 17:39:22 -05:00
|
|
|
process.task.onPrint = function(args) {
|
2022-08-14 12:58:26 -04:00
|
|
|
imports.app.print(...args);
|
2022-02-13 17:39:22 -05:00
|
|
|
};
|
2021-01-02 13:10:00 -05:00
|
|
|
process.task.onError = function(error) {
|
|
|
|
try {
|
2022-09-03 21:25:16 -04:00
|
|
|
process.app.makeFunction(['error'])(error);
|
2021-01-02 13:10:00 -05:00
|
|
|
} catch(e) {
|
|
|
|
print(e);
|
2016-04-16 17:30:52 -04:00
|
|
|
}
|
2021-01-02 13:10:00 -05:00
|
|
|
};
|
|
|
|
imports.ssb = Object.fromEntries(Object.keys(ssb).map(key => [key, ssb[key].bind(ssb)]));
|
2022-07-13 21:01:14 -04:00
|
|
|
imports.ssb.createIdentity = function() {
|
|
|
|
if (process.credentials &&
|
|
|
|
process.credentials.session &&
|
|
|
|
process.credentials.session.name) {
|
|
|
|
return ssb.createIdentity(process.credentials.session.name);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
imports.ssb.getIdentities = function() {
|
|
|
|
if (process.credentials &&
|
|
|
|
process.credentials.session &&
|
|
|
|
process.credentials.session.name) {
|
|
|
|
return ssb.getIdentities(process.credentials.session.name);
|
|
|
|
}
|
|
|
|
};
|
2022-07-31 15:01:08 -04:00
|
|
|
imports.ssb.appendMessageWithIdentity = function(id, message) {
|
|
|
|
if (process.credentials &&
|
|
|
|
process.credentials.session &&
|
|
|
|
process.credentials.session.name) {
|
2022-08-14 13:34:27 -04:00
|
|
|
return imports.core.permissionTest('ssb_append').then(function() {
|
|
|
|
return ssb.appendMessageWithIdentity(process.credentials.session.name, id, message);
|
2022-08-13 21:46:11 -04:00
|
|
|
});
|
2022-07-31 15:01:08 -04:00
|
|
|
}
|
|
|
|
};
|
2022-07-31 15:45:28 -04:00
|
|
|
|
2021-01-02 13:10:00 -05:00
|
|
|
if (process.credentials &&
|
|
|
|
process.credentials.session &&
|
|
|
|
process.credentials.session.name) {
|
|
|
|
imports.database = function(key) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let db = new Database(process.credentials.session.name + ':' + key);
|
2021-01-02 13:10:00 -05:00
|
|
|
return Object.fromEntries(Object.keys(db).map(x => [x, db[x].bind(db)]));
|
|
|
|
};
|
2022-05-25 19:45:52 -04:00
|
|
|
imports.my_shared_database = function(packageName, key) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let db = new Database(':shared:' + process.credentials.session.name + ':' + packageName + ':' + key);
|
2022-05-25 19:45:52 -04:00
|
|
|
return Object.fromEntries(Object.keys(db).map(x => [x, db[x].bind(db)]));
|
|
|
|
};
|
|
|
|
imports.databases = function() {
|
|
|
|
return [].concat(databases.list(':shared:' + process.credentials.session.name + ':%'), databases.list(process.credentials.session.name + ':%'));
|
|
|
|
};
|
2016-04-09 11:24:48 -04:00
|
|
|
}
|
2022-03-15 20:23:14 -04:00
|
|
|
if (options.packageOwner && options.packageName) {
|
|
|
|
imports.shared_database = function(key) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let db = new Database(':shared:' + options.packageOwner + ':' + options.packageName + ':' + key);
|
2022-03-15 20:23:14 -04:00
|
|
|
return Object.fromEntries(Object.keys(db).map(x => [x, db[x].bind(db)]));
|
|
|
|
}
|
|
|
|
}
|
2022-08-14 14:24:41 -04:00
|
|
|
process.sendPermissions = function sendPermissions() {
|
|
|
|
process.app.send({action: 'permissions', permissions: imports.core.permissionsGranted()});
|
|
|
|
}
|
|
|
|
process.resetPermission = function resetPermission(permission) {
|
|
|
|
let user = process?.credentials?.session?.name;
|
|
|
|
storePermission(user, options?.packageOwner, options?.packageName, permission, undefined);
|
|
|
|
process.sendPermissions();
|
|
|
|
}
|
2016-03-12 13:50:43 -05:00
|
|
|
process.task.setImports(imports);
|
|
|
|
process.task.activate();
|
2021-01-09 18:06:33 -05:00
|
|
|
let source = await getBlobOrContent(blobId);
|
2023-01-28 17:44:45 -05:00
|
|
|
let appSourceName = blobId;
|
|
|
|
let appSource = utf8Decode(source);
|
2021-01-02 13:10:00 -05:00
|
|
|
try {
|
2023-01-28 17:44:45 -05:00
|
|
|
let appObject = JSON.parse(appSource);
|
2022-03-17 21:24:29 -04:00
|
|
|
if (appObject.type == "tildefriends-app") {
|
2022-01-30 16:09:32 -05:00
|
|
|
appSourceName = 'app.js';
|
2023-01-28 17:44:45 -05:00
|
|
|
let id = appObject.files[appSourceName];
|
|
|
|
let blob = await getBlobOrContent(id);
|
2021-01-02 13:10:00 -05:00
|
|
|
appSource = utf8Decode(blob);
|
2022-06-19 14:01:21 -04:00
|
|
|
await process.task.loadFile(['/tfrpc.js', await File.readFile('core/tfrpc.js')]);
|
2022-03-17 21:24:29 -04:00
|
|
|
await Promise.all(Object.keys(appObject.files).map(async function(f) {
|
|
|
|
await process.task.loadFile([f, await getBlobOrContent(appObject.files[f])]);
|
2021-01-02 13:10:00 -05:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
printError({print: print}, e);
|
|
|
|
}
|
2017-05-23 13:47:48 -04:00
|
|
|
broadcastEvent('onSessionBegin', [getUser(process, process)]);
|
|
|
|
resolveReady(process);
|
2021-01-02 13:10:00 -05:00
|
|
|
if (process.app) {
|
|
|
|
process.app.send({action: "ready"});
|
2022-08-14 14:24:41 -04:00
|
|
|
process.sendPermissions();
|
2017-05-23 13:47:48 -04:00
|
|
|
}
|
2022-01-30 16:09:32 -05:00
|
|
|
await process.task.execute({name: appSourceName, source: appSource});
|
2016-03-12 13:50:43 -05:00
|
|
|
} catch (error) {
|
2022-09-10 21:56:29 -04:00
|
|
|
if (process?.task?.onError) {
|
|
|
|
process.task.onError(error);
|
2022-01-02 21:25:11 -05:00
|
|
|
} else {
|
|
|
|
printError({print: print}, error);
|
|
|
|
}
|
2016-03-12 13:50:43 -05:00
|
|
|
rejectReady();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return process;
|
|
|
|
}
|
|
|
|
|
|
|
|
function setGlobalSettings(settings) {
|
2021-10-27 19:27:21 -04:00
|
|
|
gGlobalSettings = settings;
|
2022-01-17 21:50:46 -05:00
|
|
|
try {
|
2022-02-13 17:03:12 -05:00
|
|
|
return new Database('core').set('settings', JSON.stringify(settings));
|
2022-01-17 21:50:46 -05:00
|
|
|
} catch (error) {
|
|
|
|
print('Error storing settings:', error);
|
|
|
|
}
|
2016-03-12 13:50:43 -05:00
|
|
|
}
|
|
|
|
|
2023-01-28 17:44:45 -05:00
|
|
|
let kStaticFiles = [
|
2021-01-02 13:10:00 -05:00
|
|
|
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
|
2021-12-22 09:27:52 -05:00
|
|
|
{uri: '/style.css', type: 'text/css; charset=UTF-8'},
|
|
|
|
{uri: '/favicon.png', type: 'image/png'},
|
|
|
|
{uri: '/client.js', type: 'text/javascript; charset=UTF-8'},
|
2022-06-19 14:01:21 -04:00
|
|
|
{uri: '/tfrpc.js', type: 'text/javascript; charset=UTF-8', headers: {'Access-Control-Allow-Origin': 'null'}},
|
2021-12-22 09:27:52 -05:00
|
|
|
{uri: '/robots.txt', type: 'text/plain; charset=UTF-8'},
|
2021-01-02 13:10:00 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
function startsWithBytes(data, bytes) {
|
|
|
|
if (data.byteLength >= bytes.length) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let dataBytes = new Uint8Array(data.slice(0, bytes.length));
|
|
|
|
for (let i = 0; i < bytes.length; i++) {
|
2022-01-30 15:23:55 -05:00
|
|
|
if (dataBytes[i] != bytes[i] && bytes[i] !== null) {
|
2021-01-02 13:10:00 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function staticFileHandler(request, response, blobId, uri) {
|
2023-01-28 17:44:45 -05:00
|
|
|
for (let i in kStaticFiles) {
|
2021-01-02 13:10:00 -05:00
|
|
|
if (uri === kStaticFiles[i].uri) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let path = kStaticFiles[i].path || uri.substring(1);
|
|
|
|
let type = kStaticFiles[i].type || guessType(path);
|
2022-09-14 19:18:55 -04:00
|
|
|
|
|
|
|
let stat = await File.stat('core/' + path);
|
|
|
|
let id = `${stat.mtime}_${stat.size}`;
|
|
|
|
|
|
|
|
if (request.headers['if-none-match'] === '"' + id + '"') {
|
|
|
|
response.writeHead(304, {});
|
|
|
|
response.end();
|
|
|
|
} else {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = await File.readFile('core/' + path);
|
2022-09-14 19:18:55 -04:00
|
|
|
response.writeHead(200, Object.assign(
|
|
|
|
{
|
|
|
|
'Content-Type': type,
|
|
|
|
'Content-Length': data.byteLength,
|
|
|
|
'etag': '"' + id + '"',
|
|
|
|
},
|
|
|
|
kStaticFiles[i].headers || {}));
|
|
|
|
response.end(data);
|
|
|
|
}
|
2021-01-02 13:10:00 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length});
|
|
|
|
response.end("File not found");
|
|
|
|
}
|
|
|
|
|
2022-01-02 14:10:45 -05:00
|
|
|
const k_mime_types = {
|
|
|
|
'json': 'text/json',
|
|
|
|
'js': 'text/javascript',
|
|
|
|
'html': 'text/html',
|
|
|
|
'css': 'text/css',
|
|
|
|
'map': 'application/json',
|
|
|
|
};
|
|
|
|
|
2022-06-18 13:07:36 -04:00
|
|
|
async function staticDirectoryHandler(request, response, directory, uri) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let filename = uri || 'index.html';
|
2022-01-02 14:10:45 -05:00
|
|
|
if (filename.indexOf('..') != -1) {
|
|
|
|
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length});
|
|
|
|
response.end("File not found");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2022-09-14 19:18:55 -04:00
|
|
|
let stat = await File.stat(directory + filename);
|
|
|
|
let id = `${stat.mtime}_${stat.size}`;
|
|
|
|
|
|
|
|
if (request.headers['if-none-match'] === '"' + id + '"') {
|
|
|
|
response.writeHead(304, {});
|
|
|
|
response.end();
|
|
|
|
} else {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = await File.readFile(directory + filename);
|
2022-09-14 19:18:55 -04:00
|
|
|
response.writeHead(200, {
|
|
|
|
'Content-Type': k_mime_types[filename.split('.').pop()] || 'text/plain',
|
|
|
|
'Content-Length': data.byteLength,
|
|
|
|
'etag': '"' + id + '"',
|
|
|
|
});
|
|
|
|
response.end(data);
|
|
|
|
}
|
2021-12-21 14:53:30 -05:00
|
|
|
} catch {
|
|
|
|
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length});
|
|
|
|
response.end("File not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 20:58:12 -05:00
|
|
|
async function wellKnownHandler(request, response, path) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = await File.readFile("data/global/.well-known/" + path);
|
2022-01-04 20:58:12 -05:00
|
|
|
if (data) {
|
|
|
|
response.writeHead(200, {"Content-Type": "text/plain", "Content-Length": data.length});
|
|
|
|
response.end(data);
|
|
|
|
} else {
|
|
|
|
response.writeHead(404, {"Content-Type": "text/plain", "Content-Length": "File not found".length});
|
|
|
|
response.end("File not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-15 14:05:59 -05:00
|
|
|
function sendData(response, data, type, headers) {
|
2021-01-02 13:10:00 -05:00
|
|
|
if (data) {
|
|
|
|
if (startsWithBytes(data, [0xff, 0xd8, 0xff, 0xdb]) ||
|
|
|
|
startsWithBytes(data, [0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01]) ||
|
|
|
|
startsWithBytes(data, [0xff, 0xd8, 0xff, 0xee]) ||
|
|
|
|
startsWithBytes(data, [0xff, 0xd8, 0xff, 0xe1, null, null, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00])) {
|
2022-01-15 14:05:59 -05:00
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "image/jpeg", "Content-Length": data.byteLength}, headers || {}));
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end(data);
|
|
|
|
} else if (startsWithBytes(data, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) {
|
2022-01-15 14:05:59 -05:00
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "image/png", "Content-Length": data.byteLength}, headers || {}));
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end(data);
|
|
|
|
} else if (startsWithBytes(data, [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]) ||
|
|
|
|
startsWithBytes(data, [0x47, 0x49, 0x46, 0x38, 0x39, 0x61])) {
|
2022-01-15 14:05:59 -05:00
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "image/gif", "Content-Length": data.byteLength}, headers || {}));
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end(data);
|
2022-12-30 09:51:43 -05:00
|
|
|
} else if (startsWithBytes(data, [0x52, 0x49, 0x46, 0x46, null, null, null, null, 0x57, 0x45, 0x42, 0x50])) {
|
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "image/webp", "Content-Length": data.byteLength}, headers || {}));
|
|
|
|
response.end(data);
|
2023-02-07 18:39:04 -05:00
|
|
|
} else if (startsWithBytes(data, [0x3c, 0x73, 0x76, 0x67])) {
|
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "image/svg+xml", "Content-Length": data.byteLength}, headers || {}));
|
|
|
|
response.end(data);
|
2022-06-16 11:53:13 -04:00
|
|
|
} else if (startsWithBytes(data, [null, null, null, null, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32])) {
|
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "audio/mpeg", "Content-Length": data.byteLength}, headers || {}));
|
|
|
|
response.end(data);
|
2022-11-29 22:07:30 -05:00
|
|
|
} else if (startsWithBytes(data, [null, null, null, null, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d]) ||
|
|
|
|
startsWithBytes(data, [null, null, null, null, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32])) {
|
2022-11-29 21:36:52 -05:00
|
|
|
response.writeHead(200, Object.assign({"Content-Type": "video/mp4", "Content-Length": data.byteLength}, headers || {}));
|
|
|
|
response.end(data);
|
2021-01-02 13:10:00 -05:00
|
|
|
} else {
|
2022-12-28 13:23:52 -05:00
|
|
|
response.writeHead(200, Object.assign({"Content-Type": type || "application/binary", "Content-Length": data.byteLength}, headers || {}));
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end(data);
|
|
|
|
}
|
|
|
|
} else {
|
2022-01-15 14:05:59 -05:00
|
|
|
response.writeHead(404, Object.assign({"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length}, headers || {}));
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end("File not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-09 18:06:33 -05:00
|
|
|
async function getBlobOrContent(id) {
|
|
|
|
if (!id) {
|
|
|
|
return;
|
|
|
|
} else if (id.startsWith('&')) {
|
|
|
|
return ssb.blobGet(id);
|
|
|
|
} else if (id.startsWith('%')) {
|
|
|
|
return ssb.messageContentGet(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-22 08:51:49 -05:00
|
|
|
function guessType(path) {
|
|
|
|
const k_extension_to_type = {
|
|
|
|
'css': 'text/css',
|
2021-12-22 09:27:52 -05:00
|
|
|
'html': 'text/html',
|
|
|
|
'js': 'text/javascript',
|
2022-06-18 16:51:22 -04:00
|
|
|
'svg': 'image/svg+xml',
|
2021-12-22 08:51:49 -05:00
|
|
|
};
|
2023-01-28 17:44:45 -05:00
|
|
|
let extension = path.split('.').pop();
|
2021-12-22 08:51:49 -05:00
|
|
|
return k_extension_to_type[extension];
|
|
|
|
}
|
|
|
|
|
2021-01-02 13:10:00 -05:00
|
|
|
async function blobHandler(request, response, blobId, uri) {
|
2023-01-28 17:44:45 -05:00
|
|
|
for (let i in kStaticFiles) {
|
2022-09-14 19:18:55 -04:00
|
|
|
if (uri === kStaticFiles[i].uri && kStaticFiles[i].path) {
|
|
|
|
let stat = await File.stat('core/' + kStaticFiles[i].path);
|
|
|
|
let id = `${stat.mtime}_${stat.size}`;
|
|
|
|
|
|
|
|
if (request.headers['if-none-match'] === '"' + id + '"') {
|
|
|
|
response.writeHead(304, {});
|
|
|
|
response.end();
|
|
|
|
} else {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = await File.readFile('core/' + kStaticFiles[i].path);
|
2022-09-14 19:18:55 -04:00
|
|
|
response.writeHead(200, Object.assign(
|
|
|
|
{
|
|
|
|
'Content-Type': kStaticFiles[i].type,
|
|
|
|
'Content-Length': data.byteLength,
|
|
|
|
'etag': '"' + id + '"',
|
|
|
|
},
|
|
|
|
kStaticFiles[i].headers || {}));
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end(data);
|
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
return;
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!uri) {
|
2022-01-08 20:36:29 -05:00
|
|
|
response.writeHead(303, {"Location": (request.client.tls ? 'https://' : 'http://') + request.headers.host + blobId + '/', "Content-Length": "0"});
|
2021-01-02 13:10:00 -05:00
|
|
|
response.end(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-28 17:44:45 -05:00
|
|
|
let process;
|
2022-09-14 19:18:55 -04:00
|
|
|
if (uri == "/view") {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data;
|
|
|
|
let match;
|
2022-09-14 19:18:55 -04:00
|
|
|
if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let id = await new Database(match[1]).get('path:' + match[2]);
|
2022-09-14 19:18:55 -04:00
|
|
|
if (id) {
|
|
|
|
if (request.headers['if-none-match'] === '"' + id + '"') {
|
|
|
|
response.writeHead(304, {});
|
|
|
|
response.end();
|
|
|
|
} else {
|
|
|
|
data = await getBlobOrContent(id);
|
|
|
|
if (match[3]) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let appObject = JSON.parse(data);
|
2022-09-14 19:18:55 -04:00
|
|
|
data = appObject.files[match[3]];
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
sendData(response, data, undefined, {etag: '"' + id + '"'});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (request.headers['if-none-match'] === '"' + blobId + '"') {
|
|
|
|
response.writeHead(304, {});
|
|
|
|
response.end();
|
2022-01-15 14:05:59 -05:00
|
|
|
} else {
|
2022-09-14 19:18:55 -04:00
|
|
|
sendData(response, data, undefined, {etag: '"' + blobId + '"'});
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (request.headers['if-none-match'] === '"' + blobId + '"') {
|
|
|
|
response.writeHead(304, {});
|
|
|
|
response.end();
|
2021-01-02 13:10:00 -05:00
|
|
|
} else {
|
2021-01-09 18:06:33 -05:00
|
|
|
data = await getBlobOrContent(blobId);
|
2022-09-14 19:18:55 -04:00
|
|
|
sendData(response, data, undefined, {etag: '"' + blobId + '"'});
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
}
|
|
|
|
} else if (uri == "/save") {
|
2023-01-28 17:44:45 -05:00
|
|
|
let match;
|
2022-09-14 19:18:55 -04:00
|
|
|
if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) {
|
2022-11-30 19:50:06 -05:00
|
|
|
let newBlobId = await ssb.blobStore(request.body);
|
2023-01-28 17:44:45 -05:00
|
|
|
let user = match[1];
|
|
|
|
let appName = match[2];
|
|
|
|
let credentials = auth.query(request.headers);
|
2022-09-14 19:18:55 -04:00
|
|
|
if (credentials && credentials.session &&
|
|
|
|
(credentials.session.name == user ||
|
|
|
|
(credentials.permissions.administration && user == 'core'))) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let database = new Database(user);
|
|
|
|
let apps = new Set();
|
2022-09-14 19:18:55 -04:00
|
|
|
let apps_original = database.get('apps');
|
|
|
|
try {
|
|
|
|
apps = new Set(JSON.parse(apps_original));
|
|
|
|
} catch {
|
|
|
|
}
|
|
|
|
if (!apps.has(appName)) {
|
|
|
|
apps.add(appName);
|
|
|
|
}
|
|
|
|
apps = JSON.stringify([...apps].sort());
|
|
|
|
if (apps != apps_original) {
|
|
|
|
database.set('apps', apps);
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
database.set('path:' + appName, newBlobId);
|
|
|
|
} else {
|
|
|
|
response.writeHead(401, {"Content-Type": "text/plain; charset=utf-8"});
|
|
|
|
response.end("401 Unauthorized");
|
|
|
|
return;
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2022-11-30 19:50:06 -05:00
|
|
|
|
2022-12-02 21:18:48 -05:00
|
|
|
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"});
|
|
|
|
response.end("/" + newBlobId);
|
|
|
|
} else if (blobId === '') {
|
|
|
|
let newBlobId = await ssb.blobStore(request.body);
|
2022-11-30 19:50:06 -05:00
|
|
|
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"});
|
|
|
|
response.end("/" + newBlobId);
|
|
|
|
} else {
|
|
|
|
response.writeHead(400, {"Content-Type": "text/plain; charset=utf-8"});
|
|
|
|
response.end('Invalid name.');
|
2022-09-14 19:18:55 -04:00
|
|
|
}
|
2021-01-02 13:10:00 -05:00
|
|
|
|
2022-09-14 19:18:55 -04:00
|
|
|
} else if (uri == "/delete") {
|
|
|
|
let match;
|
|
|
|
if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let user = match[1];
|
|
|
|
let appName = match[2];
|
|
|
|
let credentials = auth.query(request.headers);
|
2022-09-14 19:18:55 -04:00
|
|
|
if (credentials && credentials.session &&
|
|
|
|
(credentials.session.name == user ||
|
|
|
|
(credentials.permissions.administration && user == 'core'))) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let database = new Database(user);
|
|
|
|
let apps = new Set();
|
2022-09-14 19:18:55 -04:00
|
|
|
try {
|
|
|
|
apps = new Set(JSON.parse(database.get('apps')));
|
|
|
|
} catch {
|
|
|
|
}
|
|
|
|
if (apps.delete(appName)) {
|
|
|
|
database.set('apps', JSON.stringify([...apps]));
|
2022-06-20 14:13:19 -04:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
database.remove('path:' + appName);
|
|
|
|
} else {
|
|
|
|
response.writeHead(401, {"Content-Type": "text/plain; charset=utf-8"});
|
|
|
|
response.end("401 Unauthorized");
|
|
|
|
return;
|
2022-06-20 14:13:19 -04:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
}
|
2022-06-20 14:13:19 -04:00
|
|
|
|
2022-09-14 19:18:55 -04:00
|
|
|
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"});
|
|
|
|
response.end('OK');
|
|
|
|
} else {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data;
|
|
|
|
let type;
|
|
|
|
let headers;
|
|
|
|
let match;
|
2022-09-14 19:18:55 -04:00
|
|
|
if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let db = new Database(match[1]);
|
|
|
|
let id = await db.get('path:' + match[2]);
|
2022-09-14 19:18:55 -04:00
|
|
|
if (id) {
|
|
|
|
if (request.headers['if-none-match'] && request.headers['if-none-match'] == '"' + id + '"') {
|
|
|
|
headers = {
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
};
|
|
|
|
response.writeHead(304, headers);
|
|
|
|
response.end();
|
2022-01-15 14:05:59 -05:00
|
|
|
} else {
|
2022-09-14 19:18:55 -04:00
|
|
|
data = utf8Decode(await getBlobOrContent(id));
|
2023-01-28 17:44:45 -05:00
|
|
|
let appObject = JSON.parse(data);
|
2022-09-14 19:18:55 -04:00
|
|
|
data = appObject.files[uri.substring(1)];
|
|
|
|
data = await getBlobOrContent(data);
|
|
|
|
type = guessType(uri);
|
|
|
|
headers = {
|
|
|
|
'ETag': '"' + id + '"',
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
};
|
2022-01-15 14:05:59 -05:00
|
|
|
sendData(response, data, type, headers);
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2021-01-09 18:06:33 -05:00
|
|
|
} else {
|
2022-01-15 14:05:59 -05:00
|
|
|
sendData(response, data, type, headers);
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
2022-09-14 19:18:55 -04:00
|
|
|
} else {
|
|
|
|
data = utf8Decode(await getBlobOrContent(blobId));
|
2023-01-28 17:44:45 -05:00
|
|
|
let appObject = JSON.parse(data);
|
2022-09-14 19:18:55 -04:00
|
|
|
data = appObject.files[uri.substring(1)];
|
|
|
|
data = await getBlobOrContent(data);
|
|
|
|
headers = {
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
};
|
|
|
|
sendData(response, data, type, headers);
|
2021-01-02 13:10:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-07 17:28:58 -05:00
|
|
|
ssb.addEventListener('broadcasts', function() {
|
2021-01-02 13:10:00 -05:00
|
|
|
broadcastEvent('onBroadcastsChanged', []);
|
2021-11-07 17:28:58 -05:00
|
|
|
});
|
2021-01-02 13:10:00 -05:00
|
|
|
|
2021-11-07 17:28:58 -05:00
|
|
|
ssb.addEventListener('connections', function() {
|
2021-01-02 13:10:00 -05:00
|
|
|
broadcastEvent('onConnectionsChanged', []);
|
2021-11-07 17:28:58 -05:00
|
|
|
});
|
2021-01-02 13:10:00 -05:00
|
|
|
|
2021-10-05 21:25:33 -04:00
|
|
|
async function loadSettings() {
|
2023-02-01 18:40:21 -05:00
|
|
|
let data = {};
|
2021-10-05 21:25:33 -04:00
|
|
|
try {
|
2023-01-28 17:44:45 -05:00
|
|
|
let settings = new Database('core').get('settings');
|
2022-01-17 21:50:46 -05:00
|
|
|
if (settings) {
|
|
|
|
data = JSON.parse(settings);
|
2017-01-29 07:30:02 -05:00
|
|
|
}
|
2021-10-05 21:25:33 -04:00
|
|
|
} catch (error) {
|
2022-01-17 21:50:46 -05:00
|
|
|
print("Settings not found in database:", error);
|
|
|
|
}
|
2023-02-01 18:40:21 -05:00
|
|
|
gGlobalSettings = data;
|
2021-10-05 21:25:33 -04:00
|
|
|
}
|
|
|
|
|
2022-01-20 21:53:15 -05:00
|
|
|
function sendStats() {
|
2023-01-27 19:14:56 -05:00
|
|
|
let apps = Object.values(gProcesses).filter(process => process.app && process.stats).map(process => process.app);
|
|
|
|
if (apps.length) {
|
|
|
|
let stats = getStats();
|
|
|
|
for (let app of apps) {
|
|
|
|
app.send({action: 'stats', stats: stats});
|
2022-01-20 21:53:15 -05:00
|
|
|
}
|
2022-02-26 16:17:15 -05:00
|
|
|
setTimeout(sendStats, 1000);
|
|
|
|
} else {
|
|
|
|
gStatsTimer = false;
|
|
|
|
}
|
2022-01-20 21:53:15 -05:00
|
|
|
}
|
|
|
|
|
2022-04-20 19:45:17 -04:00
|
|
|
function enableStats(process, enabled) {
|
|
|
|
process.stats = enabled;
|
|
|
|
if (!gStatsTimer) {
|
|
|
|
gStatsTimer = true;
|
|
|
|
sendStats();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 21:25:33 -04:00
|
|
|
loadSettings().then(function() {
|
|
|
|
httpd.all("/login", auth.handler);
|
|
|
|
httpd.all("", function(request, response) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let match;
|
2021-10-05 21:25:33 -04:00
|
|
|
if (request.uri === "/" || request.uri === "") {
|
2022-01-08 20:36:29 -05:00
|
|
|
response.writeHead(303, {"Location": (request.client.tls ? 'https://' : 'http://') + request.headers.host + gGlobalSettings.index, "Content-Length": "0"});
|
2021-10-05 21:25:33 -04:00
|
|
|
return response.end();
|
|
|
|
} else if (match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri)) {
|
|
|
|
return blobHandler(request, response, match[1], match[2]);
|
|
|
|
} else if (match = /^\/([&\%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(request.uri)) {
|
|
|
|
return blobHandler(request, response, match[1], match[2]);
|
|
|
|
} else if (match = /^\/static(\/.*)/.exec(request.uri)) {
|
|
|
|
return staticFileHandler(request, response, null, match[1]);
|
2022-06-18 13:07:36 -04:00
|
|
|
} else if (match = /^\/codemirror\/([\.\w-/]*)$/.exec(request.uri)) {
|
|
|
|
return staticDirectoryHandler(request, response, 'deps/codemirror/', match[1]);
|
2022-11-09 21:39:00 -05:00
|
|
|
} else if (match = /^\/speedscope\/([\.\w-/]*)$/.exec(request.uri)) {
|
|
|
|
return staticDirectoryHandler(request, response, 'deps/speedscope/', match[1]);
|
2022-06-18 13:07:36 -04:00
|
|
|
} else if (match = /^\/split\/([\.\w-/]*)$/.exec(request.uri)) {
|
|
|
|
return staticDirectoryHandler(request, response, 'deps/split/', match[1]);
|
|
|
|
} else if (match = /^\/smoothie\/([\.\w-/]*)$/.exec(request.uri)) {
|
|
|
|
return staticDirectoryHandler(request, response, 'deps/smoothie/', match[1]);
|
2022-06-20 14:13:19 -04:00
|
|
|
} else if (match = /^(.*)(\/(?:save|delete)?)$/.exec(request.uri)) {
|
2021-10-05 21:25:33 -04:00
|
|
|
return blobHandler(request, response, match[1], match[2]);
|
|
|
|
} else if (match = /^\/trace$/.exec(request.uri)) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = trace();
|
2022-01-02 14:10:45 -05:00
|
|
|
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.length.toString()});
|
2021-10-05 21:25:33 -04:00
|
|
|
return response.end(data);
|
2023-01-22 15:37:19 -05:00
|
|
|
} else if (match = /^\/disconnections$/.exec(request.uri)) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = utf8Encode(JSON.stringify(disconnectionsDebug(), null, 2));
|
2023-01-22 15:37:19 -05:00
|
|
|
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.byteLength.toString()});
|
|
|
|
return response.end(data);
|
2022-09-21 20:38:26 -04:00
|
|
|
} else if (match = /^\/debug$/.exec(request.uri)) {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = JSON.stringify(getDebug(), null, 2);
|
2022-09-21 20:38:26 -04:00
|
|
|
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.length.toString()});
|
|
|
|
return response.end(data);
|
2021-10-05 21:25:33 -04:00
|
|
|
} else if (request.uri == "/robots.txt") {
|
|
|
|
return blobHandler(request, response, null, request.uri);
|
|
|
|
} else if ((match = /^\/.well-known\/(.*)/.exec(request.uri)) && request.uri.indexOf("..") == -1) {
|
2022-01-04 20:58:12 -05:00
|
|
|
return wellKnownHandler(request, response, match[1]);
|
2021-10-05 21:25:33 -04:00
|
|
|
} else {
|
2023-01-28 17:44:45 -05:00
|
|
|
let data = "File not found.";
|
2021-10-05 21:25:33 -04:00
|
|
|
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Content-Length": data.length.toString()});
|
|
|
|
return response.end(data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
httpd.registerSocketHandler("/app/socket", app.socket);
|
|
|
|
}).catch(function(error) {
|
|
|
|
print('Failed to load settings.');
|
2022-06-18 13:39:08 -04:00
|
|
|
printError({print: print}, error);
|
2022-04-26 19:05:02 -04:00
|
|
|
exit(1);
|
2016-03-12 13:50:43 -05:00
|
|
|
});
|
2022-03-17 21:24:29 -04:00
|
|
|
|
2022-07-26 20:27:10 -04:00
|
|
|
function storePermission(user, packageOwner, packageName, permission, allow) {
|
|
|
|
if (!gGlobalSettings.userPermissions) {
|
|
|
|
gGlobalSettings.userPermissions = {};
|
|
|
|
}
|
|
|
|
if (!gGlobalSettings.userPermissions[user]) {
|
|
|
|
gGlobalSettings.userPermissions[user] = {};
|
|
|
|
}
|
|
|
|
if (!gGlobalSettings.userPermissions[user][packageOwner]) {
|
|
|
|
gGlobalSettings.userPermissions[user][packageOwner] = {};
|
|
|
|
}
|
|
|
|
if (!gGlobalSettings.userPermissions[user][packageOwner][packageName]) {
|
|
|
|
gGlobalSettings.userPermissions[user][packageOwner][packageName] = {};
|
|
|
|
}
|
|
|
|
if (gGlobalSettings.userPermissions[user][packageOwner][packageName][permission] !== allow) {
|
2022-08-14 14:24:41 -04:00
|
|
|
if (allow === undefined) {
|
|
|
|
delete gGlobalSettings.userPermissions[user][packageOwner][packageName][permission];
|
|
|
|
} else {
|
|
|
|
gGlobalSettings.userPermissions[user][packageOwner][packageName][permission] = allow;
|
|
|
|
}
|
2022-07-26 20:27:10 -04:00
|
|
|
setGlobalSettings(gGlobalSettings);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-18 13:39:08 -04:00
|
|
|
export {
|
|
|
|
gGlobalSettings as globalSettings,
|
|
|
|
setGlobalSettings,
|
|
|
|
enableStats,
|
|
|
|
invoke,
|
2022-07-26 20:27:10 -04:00
|
|
|
getSessionProcessBlob,
|
2022-06-18 13:39:08 -04:00
|
|
|
};
|