diff --git a/core/core.js b/core/core.js index c4e266fd..a4a272c5 100644 --- a/core/core.js +++ b/core/core.js @@ -556,15 +556,6 @@ exports.getProcessBlob = async function getProcessBlob(blobId, key, options) { ); } }; - imports.ssb.addBlock = async function (id) { - await imports.core.permissionTest('modify_blocks', `Block ${id}.`); - await ssb_internal.addBlock(id); - }; - imports.ssb.removeBlock = async function (id) { - await imports.core.permissionTest('modify_blocks', `Unblock ${id}.`); - await ssb_internal.removeBlock(id); - }; - imports.ssb.getBlocks = ssb_internal.getBlocks.bind(ssb_internal); } if ( @@ -641,6 +632,7 @@ exports.getProcessBlob = async function getProcessBlob(blobId, key, options) { }, }; ssb.registerImports(imports, process); + process.imports = imports; process.task.setImports(imports); process.task.activate(); let source = await ssb.blobGet(blobId); diff --git a/docs/usage.md b/docs/usage.md index ba112239..5543ca6e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -27,6 +27,8 @@ commands: ``` +``` + Usage: out/debug/tildefriends run [options] Run tildefriends (default). diff --git a/package-lock.json b/package-lock.json index 10d573f6..61746fa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" diff --git a/src/api.js.c b/src/api.js.c index 149fb009..0fd32153 100644 --- a/src/api.js.c +++ b/src/api.js.c @@ -796,6 +796,231 @@ static JSValue _tf_ssb_globalSettingsGet(JSContext* context, JSValueConst this_v return result; } +typedef struct _modify_block_t +{ + const char* user; + char id[k_id_base64_len]; + bool add; + bool completed; + JSValue result; + JSValue promise[2]; +} modify_block_t; + +static void _tf_ssb_modify_block_work(tf_ssb_t* ssb, void* user_data) +{ + modify_block_t* work = user_data; + sqlite3* db = tf_ssb_acquire_db_writer(ssb); + if (work->add) + { + tf_ssb_db_add_block(db, work->id); + } + else + { + tf_ssb_db_remove_block(db, work->id); + } + tf_ssb_release_db_writer(ssb, db); + work->completed = true; +} + +static void _tf_ssb_modify_block_after_work(tf_ssb_t* ssb, int status, void* user_data) +{ + modify_block_t* request = user_data; + JSContext* context = tf_ssb_get_context(ssb); + JSValue error = JS_Call(context, request->completed ? request->promise[0] : request->promise[1], JS_UNDEFINED, 1, &request->result); + tf_util_report_error(context, error); + JS_FreeValue(context, error); + JS_FreeValue(context, request->promise[0]); + JS_FreeValue(context, request->promise[1]); + JS_FreeValue(context, request->result); + tf_free((void*)request->user); + tf_free(request); +} + +typedef void(permission_test_callback_t)(JSContext* context, bool granted, JSValue value, void* user_data); + +typedef struct _permission_test_t +{ + permission_test_callback_t* callback; + void* user_data; +} permission_test_t; + +static JSValue _tf_ssb_permission_test_resolve(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) +{ + JSClassID class_id = 0; + permission_test_t* work = JS_GetAnyOpaque(data[0], &class_id); + JS_FreeValue(context, data[0]); + work->callback(context, true, argv[0], work->user_data); + tf_free(work); + return JS_UNDEFINED; +} + +static JSValue _tf_ssb_permission_test_reject(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) +{ + JSClassID class_id = 0; + permission_test_t* work = JS_GetAnyOpaque(data[0], &class_id); + JS_FreeValue(context, data[0]); + work->callback(context, false, argv[0], work->user_data); + tf_free(work); + return JS_UNDEFINED; +} + +static void _tf_ssb_permission_test(JSContext* context, JSValue process, const char* permission, const char* description, permission_test_callback_t* callback, void* user_data) +{ + permission_test_t* payload = tf_malloc(sizeof(permission_test_t)); + *payload = (permission_test_t) { + .callback = callback, + .user_data = user_data, + }; + JSValue opaque = JS_NewObject(context); + JS_SetOpaque(opaque, payload); + JSValue imports = JS_GetPropertyStr(context, process, "imports"); + JSValue core = JS_GetPropertyStr(context, imports, "core"); + JSValue permission_test = JS_GetPropertyStr(context, core, "permissionTest"); + JSValue args[] = { + JS_NewString(context, permission), + JS_NewString(context, description), + }; + JSValue promise = JS_Call(context, permission_test, imports, tf_countof(args), args); + JSValue then = JS_GetPropertyStr(context, promise, "then"); + JSValue catch = JS_GetPropertyStr(context, promise, "catch"); + JSValue resolve = JS_NewCFunctionData(context, _tf_ssb_permission_test_resolve, 1, 0, 1, &opaque); + JSValue reject = JS_NewCFunctionData(context, _tf_ssb_permission_test_reject, 1, 0, 1, &opaque); + JSValue result = JS_Call(context, then, promise, 1, &resolve); + tf_util_report_error(context, result); + JS_FreeValue(context, result); + result = JS_Call(context, catch, promise, 1, &reject); + tf_util_report_error(context, result); + JS_FreeValue(context, promise); + JS_FreeValue(context, resolve); + JS_FreeValue(context, reject); + JS_FreeValue(context, result); + JS_FreeValue(context, then); + JS_FreeValue(context, catch); + for (int i = 0; i < tf_countof(args); i++) + { + JS_FreeValue(context, args[i]); + } + JS_FreeValue(context, permission_test); + JS_FreeValue(context, core); + JS_FreeValue(context, imports); +} + +static void _tf_ssb_modify_block_start_work(JSContext* context, bool granted, JSValue value, void* user_data) +{ + tf_task_t* task = tf_task_get(context); + tf_ssb_t* ssb = tf_task_get_ssb(task); + modify_block_t* work = user_data; + work->result = JS_DupValue(context, value); + if (granted) + { + tf_ssb_run_work(ssb, _tf_ssb_modify_block_work, _tf_ssb_modify_block_after_work, work); + } + else + { + _tf_ssb_modify_block_after_work(ssb, 0, work); + } +} + +static JSValue _tf_ssb_add_block(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) +{ + const char* id = JS_ToCString(context, argv[0]); + modify_block_t* work = tf_malloc(sizeof(modify_block_t)); + *work = (modify_block_t) { + .user = _tf_ssb_get_process_credentials_session_name(context, data[0]), + .add = true, + }; + tf_string_set(work->id, sizeof(work->id), id); + JSValue result = JS_NewPromiseCapability(context, work->promise); + JS_FreeCString(context, id); + + char description[256] = ""; + snprintf(description, sizeof(description), "Block %s.", work->id); + _tf_ssb_permission_test(context, data[0], "modify_blocks", description, _tf_ssb_modify_block_start_work, work); + return result; +} + +static JSValue _tf_ssb_remove_block(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) +{ + const char* id = JS_ToCString(context, argv[0]); + modify_block_t* work = tf_malloc(sizeof(modify_block_t)); + *work = (modify_block_t) { + .user = _tf_ssb_get_process_credentials_session_name(context, data[0]), + .add = false, + }; + tf_string_set(work->id, sizeof(work->id), id); + JSValue result = JS_NewPromiseCapability(context, work->promise); + JS_FreeCString(context, id); + + char description[256] = ""; + snprintf(description, sizeof(description), "Unblock %s.", work->id); + _tf_ssb_permission_test(context, data[0], "modify_blocks", description, _tf_ssb_modify_block_start_work, work); + return result; +} + +typedef struct _block_t +{ + char id[k_id_base64_len]; + double timestamp; +} block_t; + +typedef struct _get_blocks_t +{ + block_t* blocks; + int count; + JSValue promise[2]; +} get_blocks_t; + +static void _get_blocks_callback(const char* id, double timestamp, void* user_data) +{ + get_blocks_t* work = user_data; + work->blocks = tf_resize_vec(work->blocks, sizeof(block_t) * (work->count + 1)); + work->blocks[work->count] = (block_t) { .timestamp = timestamp }; + tf_string_set(work->blocks[work->count].id, sizeof(work->blocks[work->count].id), id); + work->count++; +} + +static void _tf_ssb_get_blocks_work(tf_ssb_t* ssb, void* user_data) +{ + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + tf_ssb_db_get_blocks(db, _get_blocks_callback, user_data); + tf_ssb_release_db_reader(ssb, db); +} + +static void _tf_ssb_get_blocks_after_work(tf_ssb_t* ssb, int status, void* user_data) +{ + get_blocks_t* work = user_data; + JSContext* context = tf_ssb_get_context(ssb); + + JSValue result = JS_NewArray(context); + for (int i = 0; i < work->count; i++) + { + JSValue entry = JS_NewObject(context); + JS_SetPropertyStr(context, entry, "id", JS_NewString(context, work->blocks[i].id)); + JS_SetPropertyStr(context, entry, "timestamp", JS_NewFloat64(context, work->blocks[i].timestamp)); + JS_SetPropertyUint32(context, result, i, entry); + } + + JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result); + JS_FreeValue(context, result); + tf_util_report_error(context, error); + JS_FreeValue(context, error); + JS_FreeValue(context, work->promise[0]); + JS_FreeValue(context, work->promise[1]); + tf_free(work->blocks); + tf_free(work); +} + +static JSValue _tf_ssb_get_blocks(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) +{ + tf_task_t* task = tf_task_get(context); + tf_ssb_t* ssb = tf_task_get_ssb(task); + get_blocks_t* work = tf_malloc(sizeof(get_blocks_t)); + *work = (get_blocks_t) { 0 }; + JSValue result = JS_NewPromiseCapability(context, work->promise); + tf_ssb_run_work(ssb, _tf_ssb_get_blocks_work, _tf_ssb_get_blocks_after_work, work); + return result; +} + static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { JSValue imports = argv[0]; @@ -831,6 +1056,10 @@ static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_va { JS_SetPropertyStr(context, core, "globalSettingsDescriptions", JS_NewCFunction(context, _tf_ssb_globalSettingsDescriptions, "globalSettingsDescriptions", 0)); JS_SetPropertyStr(context, core, "globalSettingsGet", JS_NewCFunction(context, _tf_ssb_globalSettingsGet, "globalSettingsGet", 1)); + + JS_SetPropertyStr(context, ssb, "addBlock", JS_NewCFunctionData(context, _tf_ssb_add_block, 1, 0, 1, &process)); + JS_SetPropertyStr(context, ssb, "removeBlock", JS_NewCFunctionData(context, _tf_ssb_remove_block, 1, 0, 1, &process)); + JS_SetPropertyStr(context, ssb, "getBlocks", JS_NewCFunctionData(context, _tf_ssb_get_blocks, 0, 0, 1, &process)); } JS_FreeValue(context, administration); JS_FreeValue(context, permissions); diff --git a/src/http.c b/src/http.c index 4a4b56ca..94f26094 100644 --- a/src/http.c +++ b/src/http.c @@ -950,7 +950,10 @@ void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, con copy[9] = (low >> 0) & 0xff; header += 9; } - memcpy(copy + header, data, size); + if (size) + { + memcpy(copy + header, data, size); + } _http_write(request->connection, copy, header + size); tf_free(copy); } diff --git a/src/ssb.js.c b/src/ssb.js.c index 04e34e88..eb192729 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -2172,129 +2172,6 @@ static JSValue _tf_ssb_port(JSContext* context, JSValueConst this_val, int argc, return JS_NewInt32(context, tf_ssb_server_get_port(ssb)); } -typedef struct _modify_block_t -{ - char id[k_id_base64_len]; - bool add; - JSValue promise[2]; -} modify_block_t; - -static void _tf_ssb_modify_block_work(tf_ssb_t* ssb, void* user_data) -{ - modify_block_t* work = user_data; - sqlite3* db = tf_ssb_acquire_db_writer(ssb); - if (work->add) - { - tf_ssb_db_add_block(db, work->id); - } - else - { - tf_ssb_db_remove_block(db, work->id); - } - tf_ssb_release_db_writer(ssb, db); -} - -static void _tf_ssb_modify_block_after_work(tf_ssb_t* ssb, int status, void* user_data) -{ - modify_block_t* request = user_data; - JSContext* context = tf_ssb_get_context(ssb); - JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 0, NULL); - tf_util_report_error(context, error); - JS_FreeValue(context, error); - JS_FreeValue(context, request->promise[0]); - JS_FreeValue(context, request->promise[1]); - tf_free(request); -} - -static JSValue _tf_ssb_add_block(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); - const char* id = JS_ToCString(context, argv[0]); - modify_block_t* work = tf_malloc(sizeof(modify_block_t)); - *work = (modify_block_t) { .add = true }; - tf_string_set(work->id, sizeof(work->id), id); - JSValue result = JS_NewPromiseCapability(context, work->promise); - JS_FreeCString(context, id); - tf_ssb_run_work(ssb, _tf_ssb_modify_block_work, _tf_ssb_modify_block_after_work, work); - return result; -} - -static JSValue _tf_ssb_remove_block(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); - const char* id = JS_ToCString(context, argv[0]); - modify_block_t* work = tf_malloc(sizeof(modify_block_t)); - *work = (modify_block_t) { .add = false }; - tf_string_set(work->id, sizeof(work->id), id); - JSValue result = JS_NewPromiseCapability(context, work->promise); - JS_FreeCString(context, id); - tf_ssb_run_work(ssb, _tf_ssb_modify_block_work, _tf_ssb_modify_block_after_work, work); - return result; -} - -typedef struct _block_t -{ - char id[k_id_base64_len]; - double timestamp; -} block_t; - -typedef struct _get_blocks_t -{ - block_t* blocks; - int count; - JSValue promise[2]; -} get_blocks_t; - -static void _get_blocks_callback(const char* id, double timestamp, void* user_data) -{ - get_blocks_t* work = user_data; - work->blocks = tf_resize_vec(work->blocks, sizeof(block_t) * (work->count + 1)); - work->blocks[work->count] = (block_t) { .timestamp = timestamp }; - tf_string_set(work->blocks[work->count].id, sizeof(work->blocks[work->count].id), id); - work->count++; -} - -static void _tf_ssb_get_blocks_work(tf_ssb_t* ssb, void* user_data) -{ - sqlite3* db = tf_ssb_acquire_db_reader(ssb); - tf_ssb_db_get_blocks(db, _get_blocks_callback, user_data); - tf_ssb_release_db_reader(ssb, db); -} - -static void _tf_ssb_get_blocks_after_work(tf_ssb_t* ssb, int status, void* user_data) -{ - get_blocks_t* work = user_data; - JSContext* context = tf_ssb_get_context(ssb); - - JSValue result = JS_NewArray(context); - for (int i = 0; i < work->count; i++) - { - JSValue entry = JS_NewObject(context); - JS_SetPropertyStr(context, entry, "id", JS_NewString(context, work->blocks[i].id)); - JS_SetPropertyStr(context, entry, "timestamp", JS_NewFloat64(context, work->blocks[i].timestamp)); - JS_SetPropertyUint32(context, result, i, entry); - } - - JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result); - JS_FreeValue(context, result); - tf_util_report_error(context, error); - JS_FreeValue(context, error); - JS_FreeValue(context, work->promise[0]); - JS_FreeValue(context, work->promise[1]); - tf_free(work->blocks); - tf_free(work); -} - -static JSValue _tf_ssb_get_blocks(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); - get_blocks_t* work = tf_malloc(sizeof(get_blocks_t)); - *work = (get_blocks_t) { 0 }; - JSValue result = JS_NewPromiseCapability(context, work->promise); - tf_ssb_run_work(ssb, _tf_ssb_get_blocks_work, _tf_ssb_get_blocks_after_work, work); - return result; -} - void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) { JS_NewClassID(&_tf_ssb_classId); @@ -2350,9 +2227,6 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) JS_SetPropertyStr(context, object_internal, "getIdentityInfo", JS_NewCFunction(context, _tf_ssb_getIdentityInfo, "getIdentityInfo", 3)); JS_SetPropertyStr(context, object_internal, "addEventListener", JS_NewCFunction(context, _tf_ssb_add_event_listener, "addEventListener", 2)); JS_SetPropertyStr(context, object_internal, "removeEventListener", JS_NewCFunction(context, _tf_ssb_remove_event_listener, "removeEventListener", 2)); - JS_SetPropertyStr(context, object_internal, "addBlock", JS_NewCFunction(context, _tf_ssb_add_block, "addBlock", 1)); - JS_SetPropertyStr(context, object_internal, "removeBlock", JS_NewCFunction(context, _tf_ssb_remove_block, "removeBlock", 1)); - JS_SetPropertyStr(context, object_internal, "getBlocks", JS_NewCFunction(context, _tf_ssb_get_blocks, "getBlocks", 0)); JS_FreeValue(context, global); }