diff --git a/core/app.js b/core/app.js index 1960a90c..1b1ae75e 100644 --- a/core/app.js +++ b/core/app.js @@ -6,20 +6,37 @@ let g_calls = {}; let gSessionIndex = 0; +/** + * TODOC + * @returns + */ function makeSessionId() { return (gSessionIndex++).toString(); } +/** + * TODOC + * @returns + */ function App() { this._on_output = null; this._send_queue = []; return this; } +/** + * TODOC + * @param {*} callback + */ App.prototype.readOutput = function(callback) { this._on_output = callback; } +/** + * TODOC + * @param {*} api + * @returns + */ App.prototype.makeFunction = function(api) { let self = this; let result = function() { @@ -43,6 +60,10 @@ App.prototype.makeFunction = function(api) { return result; } +/** + * TODOC + * @param {*} message + */ App.prototype.send = function(message) { if (this._send_queue) { if (this._on_output) { @@ -57,11 +78,17 @@ App.prototype.send = function(message) { } } +/** + * TODOC + * @param {*} request + * @param {*} response + * @param {*} client + */ function socket(request, response, client) { let process; let options = {}; let credentials = auth.query(request.headers); - let refresh = auth.make_refresh(credentials); + let refresh = auth.makeRefresh(credentials); response.onClose = async function() { if (process && process.task) { diff --git a/core/auth.js b/core/auth.js index 580e403a..befd13a2 100644 --- a/core/auth.js +++ b/core/auth.js @@ -5,9 +5,15 @@ let gDatabase = new Database("auth"); const kRefreshInterval = 1 * 7 * 24 * 60 * 60 * 1000; +/** + * Makes a Base64 value URL safe + * @param {string} value + * @returns TODOC + */ function b64url(value) { value = value.replaceAll('+', '-').replaceAll('/', '_'); let equals = value.indexOf('='); + if (equals !== -1) { return value.substring(0, equals); } else { @@ -15,9 +21,15 @@ function b64url(value) { } } +/** + * TODOC + * @param {string} value + * @returns + */ function unb64url(value) { value = value.replaceAll('-', '+').replaceAll('_', '/'); let remainder = value.length % 4; + if (remainder == 3) { return value + '='; } else if (remainder == 2) { @@ -27,31 +39,68 @@ function unb64url(value) { } } +/** + * Creates a JSON Web Token + * @param {object} payload Object: {"name": "username"} + * @returns the JWT + */ function makeJwt(payload) { - let ids = ssb.getIdentities(':auth'); + const ids = ssb.getIdentities(':auth'); let id; + if (ids?.length) { id = ids[0]; } else { id = ssb.createIdentity(':auth'); } - let final_payload = b64url(base64Encode(JSON.stringify(Object.assign({}, payload, {exp: (new Date().valueOf()) + kRefreshInterval})))); - let jwt = [b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))), final_payload, b64url(ssb.hmacsha256sign(final_payload, ':auth', id))].join('.'); + const final_payload = b64url( + base64Encode( + JSON.stringify( + Object.assign({}, payload, {exp: (new Date().valueOf()) + kRefreshInterval} + ) + ) + ) + ); + + const jwt = [ + b64url( + base64Encode( + JSON.stringify({ + alg: 'HS256', + typ: 'JWT' + }) + ) + ), + final_payload, + b64url( + ssb.hmacsha256sign(final_payload, ':auth', id) + ) + ].join('.'); + return jwt; } +/** + * Validates a JWT ? + * @param {*} session TODOC + * @returns + */ function readSession(session) { let jwt_parts = session?.split('.'); + if (jwt_parts?.length === 3) { let [header, payload, signature] = jwt_parts; header = JSON.parse(utf8Decode(base64Decode(unb64url(header)))); + if (header.typ === 'JWT' && header.alg === 'HS256') { signature = unb64url(signature); let id = ssb.getIdentities(':auth'); + if (id?.length && ssb.hmacsha256verify(id[0], payload, signature)) { - let result = JSON.parse(utf8Decode(base64Decode(unb64url(payload)))); - let now = new Date().valueOf() + const result = JSON.parse(utf8Decode(base64Decode(unb64url(payload)))); + const now = new Date().valueOf() + if (now < result.exp) { print(`JWT valid for another ${(result.exp - now) / 1000} seconds.`); return result; @@ -67,21 +116,42 @@ function readSession(session) { } } +/** + * Check the provided password matches the hash + * @param {string} password + * @param {string} hash bcrypt hash + * @returns true if the password matches the hash + */ function verifyPassword(password, hash) { - return bCrypt.hashpw(password, hash) == hash; + return bCrypt.hashpw(password, hash) === hash; } +/** + * Hashes a password + * @param {string} password + * @returns {string} TODOC + */ function hashPassword(password) { let salt = bCrypt.gensalt(12); return bCrypt.hashpw(password, salt); } +/** + * Check if there is an administrator on the instance + * @returns TODOC + */ function noAdministrator() { - return !core.globalSettings || !core.globalSettings.permissions || !Object.keys(core.globalSettings.permissions).some(function(name) { + return !core.globalSettings || + !core.globalSettings.permissions || + !Object.keys(core.globalSettings.permissions).some(function(name) { return core.globalSettings.permissions[name].indexOf("administration") != -1; }); } +/** + * Makes a user an administrator + * @param {string} name the user's name + */ function makeAdministrator(name) { if (!core.globalSettings.permissions) { core.globalSettings.permissions = {}; @@ -92,9 +162,15 @@ function makeAdministrator(name) { if (core.globalSettings.permissions[name].indexOf("administration") == -1) { core.globalSettings.permissions[name].push("administration"); } + core.setGlobalSettings(core.globalSettings); } +/** + * TODOC + * @param {*} headers most likely an object + * @returns + */ function getCookies(headers) { let cookies = {}; @@ -111,13 +187,27 @@ function getCookies(headers) { return cookies; } +/** + * Validates a username + * @param {string} name + * @returns false | boolean[] ? + */ function isNameValid(name) { + // TODO(tasiaiso): convert this into a regex let c = name.charAt(0); return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && name.split().map(x => x >= ('a' && x <= 'z') || x >= ('A' && x <= 'Z') || x >= ('0' && x <= '9')); } +/** + * Request handler ? + * @param {*} request TODOC + * @param {*} response + * @returns + */ function handler(request, response) { + // TODO(tasiaiso): split this function let session = getCookies(request.headers).session; + if (request.uri == "/login") { let formData = form.decodeForm(request.query); if (query(request.headers)?.permissions?.authenticated) { @@ -226,6 +316,11 @@ function handler(request, response) { } } +/** + * Gets a user's permissions based on it's session ? + * @param {*} session TODOC + * @returns + */ function getPermissions(session) { let permissions; let entry = readSession(session); @@ -236,6 +331,11 @@ function getPermissions(session) { return permissions || {}; } +/** + * Get a user's permissions ? + * @param {string} userName TODOC + * @returns + */ function getPermissionsForUser(userName) { let permissions = {}; if (core.globalSettings && core.globalSettings.permissions && core.globalSettings.permissions[userName]) { @@ -246,6 +346,11 @@ function getPermissionsForUser(userName) { return permissions; } +/** + * TODOC + * @param {*} headers + * @returns + */ function query(headers) { let session = getCookies(headers).session; let entry; @@ -258,7 +363,12 @@ function query(headers) { } } -function make_refresh(credentials) { +/** + * Refreshes a JWT ? + * @param {*} credentials TODOC + * @returns + */ +function makeRefresh(credentials) { if (credentials?.session?.name) { return { token: makeJwt({name: credentials.session.name}), @@ -267,4 +377,4 @@ function make_refresh(credentials) { } } -export { handler, query, make_refresh }; +export { handler, query, makeRefresh }; diff --git a/core/client.js b/core/client.js index a85e8fac..e5263577 100644 --- a/core/client.js +++ b/core/client.js @@ -12,7 +12,7 @@ let gOriginalInput; let kErrorColor = "#dc322f"; let kStatusColor = "#fff"; -/* Functions that server-side app code can call through the app object. */ +// Functions that server-side app code can call through the app object. const k_api = { setDocument: {args: ['content'], func: api_setDocument}, postMessage: {args: ['message'], func: api_postMessage}, @@ -24,6 +24,7 @@ const k_api = { setHash: {args: ['hash'], func: api_setHash}, }; +// TODO(tasiaiso): this is only used once, move it down ? const k_global_style = css` a:link { color: #268bd2; @@ -42,6 +43,9 @@ const k_global_style = css` } `; +/** + * Class that represents the top bar + */ class TfNavigationElement extends LitElement { static get properties() { return { @@ -63,6 +67,10 @@ class TfNavigationElement extends LitElement { this.spark_lines = {}; } + /** + * TODOC + * @param {*} event + */ toggle_edit(event) { event.preventDefault(); if (editing()) { @@ -72,10 +80,20 @@ class TfNavigationElement extends LitElement { } } + /** + * TODOC + * @param {*} key + */ reset_permission(key) { send({action: "resetPermission", permission: key}); } + /** + * TODOC + * @param {*} key + * @param {*} options + * @returns + */ get_spark_line(key, options) { if (!this.spark_lines[key]) { let spark_line = document.createElement('tf-sparkline'); @@ -94,6 +112,10 @@ class TfNavigationElement extends LitElement { return this.spark_lines[key]; } + /** + * TODOC + * @returns + */ render_login() { if (this?.credentials?.session?.name) { return html`logout ${this.credentials.session.name}`; @@ -102,6 +124,10 @@ class TfNavigationElement extends LitElement { } } + /** + * TODOC + * @returns + */ render_permissions() { if (this.show_permissions) { return html` @@ -122,6 +148,10 @@ class TfNavigationElement extends LitElement { } } + /** + * TODOC + * @returns + */ render() { let self = this; return html` @@ -157,8 +187,12 @@ class TfNavigationElement extends LitElement { `; } } + customElements.define('tf-navigation', TfNavigationElement); +/** + * TODOC + */ class TfFilesElement extends LitElement { static get properties() { return { @@ -174,6 +208,10 @@ class TfFilesElement extends LitElement { this.dropping = 0; } + /** + * TODOC + * @param {*} file + */ file_click(file) { this.dispatchEvent(new CustomEvent('file_click', { detail: { @@ -184,6 +222,11 @@ class TfFilesElement extends LitElement { })); } + /** + * TODOC + * @param {*} file + * @returns + */ render_file(file) { let classes = ['file']; if (file == this.current) { @@ -195,6 +238,10 @@ class TfFilesElement extends LitElement { return html`
this.file_click(file)}>${file}
`; } + /** + * TODOC + * @param {*} event + */ async drop(event) { event.preventDefault(); event.stopPropagation(); @@ -213,15 +260,27 @@ class TfFilesElement extends LitElement { updateFiles(); } + /** + * TODOC + * @param {*} event + */ drag_enter(event) { this.dropping++; event.preventDefault(); } + /** + * TODOC + * @param {*} event + */ drag_leave(event) { this.dropping--; } + /** + * TODOC + * @returns + */ render() { let self = this; return html` @@ -258,8 +317,12 @@ class TfFilesElement extends LitElement { `; } } + customElements.define('tf-files', TfFilesElement); +/** + * TODOC + */ class TfFilesPaneElement extends LitElement { static get properties() { return { @@ -275,11 +338,19 @@ class TfFilesPaneElement extends LitElement { this.files = {}; } + /** + * TODOC + * @param {*} expanded + */ set_expanded(expanded) { this.expanded = expanded; window.localStorage.setItem('files', expanded ? '1' : '0'); } + /** + * TODOC + * @returns + */ render() { let self = this; let expander = this.expanded ? @@ -302,8 +373,12 @@ class TfFilesPaneElement extends LitElement { `; } } + customElements.define('tf-files-pane', TfFilesPaneElement); +/** + * TODOC + */ class TfSparkLineElement extends LitElement { static get properties() { return { @@ -321,6 +396,11 @@ class TfSparkLineElement extends LitElement { this.k_values_max = 100; } + /** + * TODOC + * @param {*} key + * @param {*} value + */ append(key, value) { let line = null; for (let it of this.lines) { @@ -345,6 +425,11 @@ class TfSparkLineElement extends LitElement { this.requestUpdate(); } + /** + * TODOC + * @param {*} line + * @returns + */ render_line(line) { if (line?.values?.length >= 2) { let max = Math.max(this.max, ...line.values); @@ -353,6 +438,10 @@ class TfSparkLineElement extends LitElement { } } + /** + * TODOC + * @returns + */ render() { let max = Math.round(10.0 * Math.max(...this.lines.map(line => line.values[line.values.length - 1]))) / 10.0; return html` @@ -363,8 +452,10 @@ class TfSparkLineElement extends LitElement { `; } } + customElements.define('tf-sparkline', TfSparkLineElement); +// TODOC window.addEventListener("keydown", function(event) { if (event.keyCode == 83 && (event.altKey || event.ctrlKey)) { if (editing()) { @@ -379,6 +470,12 @@ window.addEventListener("keydown", function(event) { } }); +/** + * TODOC + * @param {*} nodes + * @param {*} callback + * @returns + */ function ensureLoaded(nodes, callback) { if (!nodes.length) { callback(); @@ -416,14 +513,26 @@ function ensureLoaded(nodes, callback) { } } +/** + * TODOC + * @returns + */ function editing() { return document.getElementById("editPane").style.display != 'none'; } +/** + * TODOC + * @returns + */ function is_edit_only() { return window.location.search == '?editonly=1' || window.innerWidth < 1024; } +/** + * TODOC + * @returns + */ async function edit() { if (editing()) { return; @@ -446,16 +555,30 @@ async function edit() { } } +/** + * TODOC + */ function trace() { window.open(`/speedscope/#profileURL=${encodeURIComponent('/trace')}`); } +/** + * TODOC + * @param {*} name + * @returns + */ function guessMode(name) { return name.endsWith(".js") ? "javascript" : name.endsWith(".html") ? "htmlmixed" : null; } +/** + * TODOC + * @param {*} name + * @param {*} id + * @returns + */ function loadFile(name, id) { return fetch('/' + id + '/view').then(function(response) { if (!response.ok) { @@ -472,6 +595,11 @@ function loadFile(name, id) { }); } +/** + * TODOC + * @param {*} path + * @returns + */ async function load(path) { let response = await fetch((path || url()) + 'view'); let json; @@ -509,16 +637,28 @@ async function load(path) { return Promise.all(promises); } +/** + * TODOC + */ function closeEditor() { window.localStorage.setItem('editing', '0'); document.getElementById("editPane").style.display = 'none'; document.getElementById('viewPane').style.display = 'flex'; } +/** + * TODOC + * @returns + */ function explodePath() { return /^\/~([^\/]+)\/([^\/]+)(.*)/.exec(window.location.pathname); } +/** + * TODOC + * @param {*} save_to + * @returns + */ function save(save_to) { document.getElementById("save").disabled = true; if (gCurrentFile) { @@ -603,6 +743,9 @@ function save(save_to) { }); } +/** + * TODOC + */ function changeIcon() { let value = prompt('Enter a new app icon emoji:'); if (value !== undefined) { @@ -611,6 +754,9 @@ function changeIcon() { } } +/** + * TODOC + */ function deleteApp() { let name = document.getElementById("name"); let path = name && name.value ? name.value : url(); @@ -627,6 +773,10 @@ function deleteApp() { } } +/** + * TODOC + * @returns + */ function url() { let hash = window.location.href.indexOf('#'); let question = window.location.href.indexOf('?'); @@ -642,20 +792,36 @@ function url() { return end != -1 ? window.location.href.substring(0, end) : window.location.href; } +/** + * TODOC + * @returns + */ function hash() { return window.location.hash != "#" ? window.location.hash : ""; } +/** + * TODOC + * @param {*} content + */ function api_setDocument(content) { let iframe = document.getElementById("document"); iframe.srcdoc = content; } +/** + * TODOC + * @param {*} message + */ function api_postMessage(message) { let iframe = document.getElementById("document"); iframe.contentWindow.postMessage(message, "*"); } +/** + * TODOC + * @param {*} error + */ function api_error(error) { if (error) { if (typeof(error) == 'string') { @@ -667,14 +833,30 @@ function api_error(error) { console.log('error', error); } +/** + * TODOC + * @param {*} key + * @param {*} value + */ function api_localStorageSet(key, value) { window.localStorage.setItem('app:' + key, value); } +/** + * TODOC + * @param {*} key + * @returns + */ function api_localStorageGet(key) { return window.localStorage.getItem('app:' + key); } +/** + * TODOC + * @param {*} permission + * @param {*} id + * @returns + */ function api_requestPermission(permission, id) { let outer = document.createElement('div'); outer.classList.add('permissions'); @@ -739,14 +921,25 @@ function api_requestPermission(permission, id) { }); } +/** + * TODOC + */ function api_print() { console.log('app>', ...arguments); } +/** + * TODOC + * @param {*} hash + */ function api_setHash(hash) { window.location.hash = hash; } +/** + * TODOC + * @param {*} message + */ function _receive_websocket_message(message) { if (message && message.action == "session") { setStatusMessage("🟢 Executing...", kStatusColor); @@ -827,10 +1020,19 @@ function _receive_websocket_message(message) { } } +/** + * TODOC + * @param {*} message + * @param {*} color + */ function setStatusMessage(message, color) { document.getElementsByTagName('tf-navigation')[0].status = {message: message, color: color}; } +/** + * TODOC + * @param {*} value + */ function send(value) { try { if (gSocket && gSocket.readyState == gSocket.OPEN) { @@ -841,6 +1043,13 @@ function send(value) { } } +/** + * TODOC + * @param {*} sourceData + * @param {*} maxWidth + * @param {*} maxHeight + * @param {*} callback + */ function fixImage(sourceData, maxWidth, maxHeight, callback) { let result = sourceData; let image = new Image(); @@ -864,16 +1073,26 @@ function fixImage(sourceData, maxWidth, maxHeight, callback) { image.src = sourceData; } +/** + * TODOC + * @param {*} image + */ function sendImage(image) { fixImage(image, 320, 240, function(result) { send({image: result}); }); } +/** + * TODOC + */ function hashChange() { send({event: 'hashChange', hash: window.location.hash}); } +/** + * TODOC + */ function focus() { if (gSocket && gSocket.readyState == gSocket.CLOSED) { connectSocket(); @@ -882,12 +1101,19 @@ function focus() { } } +/** + * TODOC + */ function blur() { if (gSocket && gSocket.readyState == gSocket.OPEN) { send({event: "blur"}); } } +/** + * TODOC + * @param {*} event + */ function message(event) { if (event.data && event.data.event == "resizeMe" && event.data.width && event.data.height) { let iframe = document.getElementById("iframe_" + event.data.name); @@ -916,6 +1142,10 @@ function message(event) { } } +/** + * TODOC + * @param {*} path + */ function reconnect(path) { let oldSocket = gSocket; gSocket = null @@ -928,6 +1158,10 @@ function reconnect(path) { connectSocket(path); } +/** + * TODOC + * @param {*} path + */ function connectSocket(path) { if (!gSocket || gSocket.readyState != gSocket.OPEN) { if (gSocket) { @@ -979,6 +1213,10 @@ function connectSocket(path) { } } +/** + * TODOC + * @param {*} name + */ function openFile(name) { let newDoc = (name && gFiles[name]) ? gFiles[name].doc : cm6.EditorState.create({doc: "", extensions: cm6.extensions}); let oldDoc = gEditor.state; @@ -995,6 +1233,9 @@ function openFile(name) { gEditor.focus(); } +/** + * TODOC + */ function updateFiles() { let files = document.getElementsByTagName("tf-files-pane")[0]; if (files) { @@ -1006,6 +1247,10 @@ function updateFiles() { gEditor.focus(); } +/** + * TODOC + * @param {*} name + */ function makeNewFile(name) { gFiles[name] = { doc: cm6.EditorState.create({extensions: cm6.extensions}), @@ -1013,6 +1258,9 @@ function makeNewFile(name) { openFile(name); } +/** + * TODOC + */ function newFile() { let name = prompt("Name of new file:", "file.js"); if (name && !gFiles[name]) { @@ -1020,6 +1268,9 @@ function newFile() { } } +/** + * TODOC + */ function removeFile() { if (confirm("Remove " + gCurrentFile + "?")) { delete gFiles[gCurrentFile]; @@ -1027,6 +1278,9 @@ function removeFile() { } } +/** + * TODOC + */ async function appExport() { let JsZip = (await import('/static/jszip.min.js')).default; let owner = window.location.pathname.split('/')[1].replace('~', ''); @@ -1049,6 +1303,12 @@ async function appExport() { a.click(); } +/** + * TODOC + * @param {*} name + * @param {*} file + * @returns + */ async function save_file_to_blob_id(name, file) { console.log(`Saving ${name}.`); let response = await fetch('/save', { @@ -1068,6 +1328,9 @@ async function save_file_to_blob_id(name, file) { return blob_id; } +/** + * TODOC + */ async function appImport() { let JsZip = (await import('/static/jszip.min.js')).default; let input = document.createElement('input'); @@ -1119,6 +1382,9 @@ async function appImport() { } } +/** + * + */ async function sourcePretty() { let prettier = (await import('/prettier/standalone.mjs')).default; let babel = (await import('/prettier/babel.mjs')).default; @@ -1140,6 +1406,7 @@ async function sourcePretty() { } } +// TODOC window.addEventListener("load", function() { window.addEventListener("hashchange", hashChange); window.addEventListener("focus", focus); diff --git a/core/core.js b/core/core.js index ea77fea2..8d90f141 100644 --- a/core/core.js +++ b/core/core.js @@ -91,6 +91,11 @@ let gGlobalSettings = { let kPingInterval = 60 * 1000; +/** + * TODOC + * @param {*} out + * @param {*} error + */ function printError(out, error) { if (error.stackTrace) { out.print(error.fileName + ":" + error.lineNumber + ": " + error.message); @@ -103,6 +108,12 @@ function printError(out, error) { } } +/** + * TODOC + * @param {*} handlers + * @param {*} argv + * @returns + */ function invoke(handlers, argv) { let promises = []; if (handlers) { @@ -119,6 +130,12 @@ function invoke(handlers, argv) { return Promise.all(promises); } +/** + * TODOC + * @param {*} eventName + * @param {*} argv + * @returns + */ function broadcastEvent(eventName, argv) { let promises = []; for (let process of Object.values(gProcesses)) { @@ -128,7 +145,11 @@ function broadcastEvent(eventName, argv) { } return Promise.all(promises); } - +/** + * TODOC + * @param {*} message + * @returns + */ function broadcast(message) { let sender = this; let promises = []; @@ -143,6 +164,12 @@ function broadcast(message) { return Promise.all(promises); } +/** + * TODOC + * @param {*} caller + * @param {*} process + * @returns + */ function getUser(caller, process) { return { key: process.key, @@ -153,6 +180,12 @@ function getUser(caller, process) { }; } +/** + * TODOC + * @param {*} user + * @param {*} process + * @returns + */ function getApps(user, process) { if (process.credentials && process.credentials.session && @@ -174,12 +207,26 @@ function getApps(user, process) { return {}; } +/** + * TODOC + * @param {*} from + * @param {*} to + * @param {*} message + * @returns + */ function postMessageInternal(from, to, message) { if (to.eventHandlers['message']) { return invoke(to.eventHandlers['message'], [getUser(from, from), message]); } } +/** + * TODOC + * @param {*} blobId + * @param {*} session + * @param {*} options + * @returns + */ async function getSessionProcessBlob(blobId, session, options) { let actualOptions = {timeout: kPingInterval}; if (options) { @@ -190,7 +237,15 @@ async function getSessionProcessBlob(blobId, session, options) { return getProcessBlob(blobId, 'session_' + session, actualOptions); } +/** + * TODOC + * @param {*} blobId + * @param {*} key + * @param {*} options + * @returns + */ async function getProcessBlob(blobId, key, options) { + // TODO(tasiaiso): break this down let process = gProcesses[key]; if (!process && !(options && "create" in options && !options.create)) { @@ -535,6 +590,11 @@ async function getProcessBlob(blobId, key, options) { return process; } +/** + * TODOC + * @param {*} settings + * @returns + */ function setGlobalSettings(settings) { gGlobalSettings = settings; try { @@ -544,6 +604,12 @@ function setGlobalSettings(settings) { } } +/** + * TODOC + * @param {*} data + * @param {*} bytes + * @returns + */ function startsWithBytes(data, bytes) { if (data.byteLength >= bytes.length) { let dataBytes = new Uint8Array(data.slice(0, bytes.length)); @@ -556,11 +622,21 @@ function startsWithBytes(data, bytes) { } } +/** + * TODOC + * @param {*} path + * @returns + */ function guessTypeFromName(path) { let extension = path.split('.').pop(); return k_mime_types[extension]; } +/** + * TODOC + * @param {*} data + * @returns + */ function guessTypeFromMagicBytes(data) { for (let magic of k_magic_bytes) { if (startsWithBytes(data, magic.bytes)) { @@ -569,6 +645,14 @@ function guessTypeFromMagicBytes(data) { } } +/** + * TODOC + * @param {*} response + * @param {*} data + * @param {*} type + * @param {*} headers + * @param {*} status_code + */ function sendData(response, data, type, headers, status_code) { if (data) { response.writeHead(status_code ?? 200, Object.assign({"Content-Type": type || guessTypeFromMagicBytes(data) || "application/binary", "Content-Length": data.byteLength}, headers || {})); @@ -579,6 +663,11 @@ function sendData(response, data, type, headers, status_code) { } } +/** + * TODOC + * @param {*} id + * @returns + */ async function getBlobOrContent(id) { if (!id) { return; @@ -590,6 +679,18 @@ async function getBlobOrContent(id) { } let g_handler_index = 0; + +/** + * TODOC + * @param {*} response + * @param {*} handler_blob_id + * @param {*} path + * @param {*} query + * @param {*} headers + * @param {*} packageOwner + * @param {*} packageName + * @returns + */ async function useAppHandler(response, handler_blob_id, path, query, headers, packageOwner, packageName) { print('useAppHandler', packageOwner, packageName); let do_resolve; @@ -623,7 +724,16 @@ async function useAppHandler(response, handler_blob_id, path, query, headers, pa return result; } +/** + * TODOC + * @param {*} request + * @param {*} response + * @param {*} blobId + * @param {*} uri + * @returns + */ async function blobHandler(request, response, blobId, uri) { + // TODO(tasiaiso): break this down ? for (let i in k_static_files) { if (uri === k_static_files[i].uri && k_static_files[i].path) { let stat = await File.stat('core/' + k_static_files[i].path); @@ -851,6 +961,9 @@ ssb.addEventListener('connections', function() { broadcastEvent('onConnectionsChanged', []); }); +/** + * TODOC + */ async function loadSettings() { let data = {}; try { @@ -869,6 +982,9 @@ async function loadSettings() { gGlobalSettings = data; } +/** + * TODOC + */ function sendStats() { let apps = Object.values(gProcesses).filter(process => process.app && process.stats).map(process => process.app); if (apps.length) { @@ -882,6 +998,11 @@ function sendStats() { } } +/** + * TODOC + * @param {*} process + * @param {*} enabled + */ function enableStats(process, enabled) { process.stats = enabled; if (!gStatsTimer) { @@ -890,6 +1011,9 @@ function enableStats(process, enabled) { } } +/** + * TODOC + */ loadSettings().then(function() { if (tildefriends.https_port && gGlobalSettings.http_redirect) { httpd.set_http_redirect(gGlobalSettings.http_redirect); @@ -955,6 +1079,14 @@ loadSettings().then(function() { exit(1); }); +/** + * TODOC + * @param {*} user + * @param {*} packageOwner + * @param {*} packageName + * @param {*} permission + * @param {*} allow + */ function storePermission(user, packageOwner, packageName, permission, allow) { if (!gGlobalSettings.userPermissions) { gGlobalSettings.userPermissions = {}; diff --git a/core/form.js b/core/form.js index 77e8e120..ff91ce64 100644 --- a/core/form.js +++ b/core/form.js @@ -1,3 +1,8 @@ +/** + * TODOC + * @param {*} encoded + * @returns + */ function decode(encoded) { let result = ""; for (let i = 0; i < encoded.length; i++) { @@ -14,6 +19,12 @@ function decode(encoded) { return result; } +/** + * TODOC + * @param {*} encoded + * @param {*} initial + * @returns + */ function decodeForm(encoded, initial) { let result = initial || {}; if (encoded) { diff --git a/core/http.js b/core/http.js index eb820977..dd861d1a 100644 --- a/core/http.js +++ b/core/http.js @@ -1,3 +1,9 @@ +/** + * TODOC + * TODO: document so we can make this improve + * @param {*} url + * @returns + */ function parseUrl(url) { // XXX: Hack. let match = url.match(new RegExp("(\\w+)://([^/:]+)(?::(\\d+))?(.*)")); @@ -9,6 +15,11 @@ function parseUrl(url) { }; } +/** + * TODOC + * @param {*} data + * @returns + */ function parseResponse(data) { let firstLine; let headers = {}; @@ -28,6 +39,13 @@ function parseResponse(data) { return {body: data}; } +/** + * TODOC + * @param {*} url + * @param {*} options + * @param {*} allowed_hosts + * @returns + */ export function fetch(url, options, allowed_hosts) { let parsed = parseUrl(url); return new Promise(function(resolve, reject) { diff --git a/core/tfrpc.js b/core/tfrpc.js index f1bf48ec..c1bdee3b 100644 --- a/core/tfrpc.js +++ b/core/tfrpc.js @@ -3,6 +3,10 @@ let g_api = {}; let g_next_id = 1; let g_calls = {}; +/** + * TODOC + * @returns + */ function get_is_browser() { try { return window !== undefined && console !== undefined; } catch { return false; } } @@ -11,6 +15,13 @@ if (k_is_browser) { print = console.log; } +/** + * TODOC + * @param {*} target + * @param {*} prop + * @param {*} receiver + * @returns + */ function make_rpc(target, prop, receiver) { return function() { let id = g_next_id++; @@ -29,6 +40,10 @@ function make_rpc(target, prop, receiver) { } } +/** + * TODOC + * @param {*} response + */ function send(response) { if (k_is_browser) { window.parent.postMessage(response, '*'); @@ -37,6 +52,10 @@ function send(response) { } } +/** + * TODOC + * @param {*} message + */ function call_rpc(message) { if (message && message.message === 'tfrpc') { let id = message.id; @@ -85,6 +104,10 @@ if (k_is_browser) { export let rpc = new Proxy({}, {get: make_rpc}); +/** + * TODOC + * @param {*} method + */ export function register(method) { g_api[method.name] = method; }