ssb: Add some plausible API and a table for storing instance-wide blocks.

This commit is contained in:
2025-11-27 14:33:57 -05:00
parent 759b522cd1
commit ddc4603f13
4 changed files with 139 additions and 11 deletions

View File

@@ -494,6 +494,7 @@ async function getProcessBlob(blobId, key, options) {
);
}
};
if (process.credentials?.permissions?.administration) {
imports.ssb.swapWithServerIdentity = function (id) {
if (
process.credentials &&
@@ -506,6 +507,21 @@ 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);
}
}
if (
process.credentials &&

View File

@@ -271,7 +271,6 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
")");
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS identities_user ON identities (user, public_key)");
_tf_ssb_db_exec(db, "DELETE FROM identities WHERE user = ':auth'");
_tf_ssb_db_exec(db,
"CREATE TABLE IF NOT EXISTS invites ("
" invite_public_key TEXT PRIMARY KEY,"
@@ -279,6 +278,11 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
" use_count INTEGER,"
" expires INTEGER"
")");
_tf_ssb_db_exec(db,
"CREATE TABLE IF NOT EXISTS blocks ("
" id TEXT PRIMARY KEY,"
" timestamp REAL"
")");
bool populate_fts = false;
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_fts')"))
@@ -2868,3 +2872,35 @@ tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* u
tf_free(info);
return copy;
}
void tf_ssb_db_add_block(sqlite3* db, const char* id)
{
sqlite3_stmt* statement = NULL;
if (sqlite3_prepare_v2(db, "INSERT INTO blocks (id, timestamp) VALUES (?, ?) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) != SQLITE_DONE)
{
tf_printf("add block: %s\n", sqlite3_errmsg(db));
}
}
sqlite3_finalize(statement);
}
}
void tf_ssb_db_remove_block(sqlite3* db, const char* id)
{
sqlite3_stmt* statement = NULL;
if (sqlite3_prepare_v2(db, "DELETE FROM blocks WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) != SQLITE_DONE)
{
tf_printf("remove block: %s\n", sqlite3_errmsg(db));
}
}
sqlite3_finalize(statement);
}
}

View File

@@ -617,4 +617,18 @@ tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* u
*/
void tf_ssb_db_add_blob_wants(sqlite3* db, const char* id);
/**
** Add an instance-wide block.
** @param db The database.
** @param id The account, message, or blob ID to block.
*/
void tf_ssb_db_add_block(sqlite3* db, const char* id);
/**
** Remove an instance-wide block.
** @param db The database.
** @param id The account, message, or blob ID to unblock.
*/
void tf_ssb_db_remove_block(sqlite3* db, const char* id);
/** @} */

View File

@@ -2172,6 +2172,66 @@ 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;
}
void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
{
JS_NewClassID(&_tf_ssb_classId);
@@ -2227,6 +2287,8 @@ 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_FreeValue(context, global);
}