diff --git a/apps/admin.json b/apps/admin.json
index b04c06cae..ac0280b49 100644
--- a/apps/admin.json
+++ b/apps/admin.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&uhGJsy5+qBgOgEgMqCTDasK+C+GWGptHKfPiAsD5eGA=.sha256","index.html":"&D3JwdPXy/QsLXkmwNDrBFXdzxfqO1/JGxfqEArnS5v4=.sha256","lit.min.js":"&3FfrVflmGr0n4lvN0GriN1Qz1lEw31SbZxRSJrcXR28=.sha256","script.js":"&TZ2ymD6cFVUjQleGcDslt8apjp7k3xLlfv2F8rQVM4I=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/api.json b/apps/api.json
index febd470f9..ac0280b49 100644
--- a/apps/api.json
+++ b/apps/api.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&p35JmopfHf8hFh3Y9x6LrIxiUwaJZ5Nabzi2sVXpKoo=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/apps.json b/apps/apps.json
index fcb285d6c..ac0280b49 100644
--- a/apps/apps.json
+++ b/apps/apps.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&qEJDfZ43KazIxiZl8OCKb2uaDOsPkxnIohEzQ1LLFpg=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/db.json b/apps/db.json
index c475e39de..ac0280b49 100644
--- a/apps/db.json
+++ b/apps/db.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&V5o5IM9/OUyIsVkjkMW/X0i/tflQOSVJuJBmHdMT9aM=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/docs.json b/apps/docs.json
index ee03c731e..ac0280b49 100644
--- a/apps/docs.json
+++ b/apps/docs.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&WEvJYebSMi5d2eXgUwJJmvR/Q4slFg3zHYB8Q2mXJII=.sha256","index.md":"&79+ntX4sRvg+MboV5nMFz01BSicxsWIQRx719VHS8uk=.sha256","todo.md":"&hQABwP24zFFhdHagRMF3Am7rV2yH19e+0xJ4wnZ4kfM=.sha256","structure.md":"&jph8x/fMXKOd4I0ZiUVb0ZLTfPQ7gBWoxJPrvtX6vtw=.sha256","guide.md":"&SgnGL0+rjetY2o9A2+lVRbNvHIkqKwMnZr9gXWneIlc=.sha256","ssb.md":"&JH1JfoTaCcUifCpnAwhImKBACI0PHoLhoOw1WAnWpLw=.sha256","vision.md":"&v2wu2MGlhNvaALQQ9rGna7ZeEQWSghFgQcDfD5xEyE0=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/follow.json b/apps/follow.json
index eaef11d62..ac0280b49 100644
--- a/apps/follow.json
+++ b/apps/follow.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&3d9ABFgRwQvWsYbFv/rzimtnLDnVrWlGtdw7serFIGw=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/ssb.json b/apps/ssb.json
index 4b1181df0..ac0280b49 100644
--- a/apps/ssb.json
+++ b/apps/ssb.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&1HWTkyCc1doft6dyKF5FDxtRAErNeY25CBrfZbKPpyo=.sha256","lit-all.min.js":"&FjJo4WtPgPmzEeF6Uhkhzv1FA7tyYfYSRuQz5zwc4fU=.sha256","index.html":"&TxhFekB9ov7tf/fmkAg7x5797i27oLidhgxEfDKC0T0=.sha256","script.js":"&G8puK9Q4MngHy3D4ppcKyT49WKbHD2OCeUcAw2ghTDE=.sha256","lit-all.min.js.map":"&H9HlMb6gIKYsWSNd+Kp0HwwpPwnlagEgi8B583aOEQQ=.sha256","tf-id-picker.js":"&maN8DUFrmRxW5nsVyOAMk5k1ekcz/pfzvSS99ac3jo8=.sha256","tf-app.js":"&F0fyawIO410YFidrzFjlHeY++sZy6ledf6CAXB+45U4=.sha256","tf-message.js":"&WYh0KTqqtlZF7ahldWokUt1yLdlThO5j1g2xlpfqI3U=.sha256","tf-user.js":"&bXTedgBudTQLXEBPY9R8OLfQ/ZLpo8YRU9Oq/wuGG3Y=.sha256","tf-utils.js":"&lYNeL7cVlDgcqrfkoRIe69DHZeqSZMiHhZIieblHbU0=.sha256","commonmark.min.js":"&bfBaMLU19d1p/vPBF9hlARqDX002KXG/UOfxOahZhe4=.sha256","tf-compose.js":"&AA04w0u0erTaBbJdMmnm1mukslXzCqozpv6UQPLZszE=.sha256","emojis.json":"&h3P4pez+AI4aYdsN0dJ3pbUEFR0276t9AM20caj/W/s=.sha256","emojis.js":"&tOkUocccQWBzkNzSEf9VMltkTSHcUALYSPYVWmJMoBc=.sha256","tf-styles.js":"&2Za+CmlZKGhUnWvGQdZBe8Plxv59WZ3nX5u5rbfwwkY=.sha256","tf-profile.js":"&vRKjsnYvOiHCQahzEfznCvP5YDwUPtltlpWf+pxwZ1Y=.sha256","commonmark-linkify.js":"&X+hNNkmSRvKY86khyAun+cXksquXbMakZdINbGbx30g=.sha256","tf-tab-search.js":"&ESt2vMG19sH5j6ungKua/ZuvIGslyuWyb3juXdOCecg=.sha256","tf-tab-news.js":"&fY+thANurOKU2/RhDt411ZtkxW0nV24+hLEf00Z1sTY=.sha256","tf-tab-connections.js":"&ywqBz3w63R6naH09kZ+01A0SfmtuSfk8QPBXWsli0yg=.sha256","tf-news.js":"&Zn+vxLUqVJbo/q6RcW8ezvbdilzllvXhZRyXk8kYwL0=.sha256","tribute.css":"&9FogMzZHKXCfGb7mlh7z+/wiNZzBsOB/tKoh6MfYJno=.sha256","tribute.esm.js":"&P1wKqCfYULpR/ahSB98JP8xaxfikuZwwtT6I/SAo7/Y=.sha256","commonmark-hashtag.js":"&fudY0YdvcMjVCSZ0oiCqUt0+bVT0a06j5TcjWaCDO8E=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/apps/ssb/tf-connections.js b/apps/ssb/tf-connections.js
deleted file mode 100644
index bea179635..000000000
--- a/apps/ssb/tf-connections.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import {LitElement, html} from './lit-all.min.js';
-import * as tfrpc from '/static/tfrpc.js';
-
-class TfConnectionsElement extends LitElement {
- static get properties() {
- return {
- broadcasts: {type: Array},
- identities: {type: Array},
- connections: {type: Array},
- users: {type: Object},
- }
- }
-
- constructor() {
- super();
- let self = this;
- this.broadcasts = [];
- this.identities = [];
- this.connections = [];
- this.users = {};
- tfrpc.rpc.getAllIdentities().then(function(identities) {
- self.identities = identities || [];
- });
- }
-
- _emit_change() {
- let changed_event = new Event('change', {
- srcElement: this,
- });
- this.dispatchEvent(changed_event);
- }
-
- changed(event) {
- this.selected = event.srcElement.value;
- tfrpc.rpc.localStorageSet('whoami', this.selected);
- this._emit_change();
- }
-
- render() {
- return html`
-
Broadcasts
-
- ${this.broadcasts.map(x => html` `)}
-
- Connections
-
- ${this.connections.map(x => html` `)}
-
- Local Accounts
-
- ${this.identities.map(x => html` `)}
-
- `;
- }
-}
-
-customElements.define('tf-connections', TfConnectionsElement);
\ No newline at end of file
diff --git a/apps/todo.json b/apps/todo.json
index 839542318..ac0280b49 100644
--- a/apps/todo.json
+++ b/apps/todo.json
@@ -1 +1,3 @@
-{"type":"tildefriends-app","files":{"app.js":"&QUR1tKa15B5Or8AfPX/8Zs87teSeX0Mh/HF7PEPBom0=.sha256","index.html":"&QXhwvxhHc9fa8iL6088hGDu9FgWdY7wkXgvU2BMNv0A=.sha256","lit-core.min.js":"&tP9KhbgwF1chFqPtkNZ12Yx9AfkpnSjFiPcX5Pw5J9g=.sha256","script.js":"&KgOaUVjBM4MzSy7PpUVQHETuvgXAx2JGPJABksBg+QY=.sha256"}}
\ No newline at end of file
+{
+ "type": "tildefriends-app"
+}
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
index 0c151be62..3b2ad6ee3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -257,12 +257,12 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
xoptOption options[] = {
{ "db-path", 'd', offsetof(args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
- { "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose apps will be exported (default: \"cory\")." },
+ { "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose apps will be exported (default: \"core\")." },
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
XOPT_NULLOPTION,
};
- args_t args = { .user = "cory" };
+ args_t args = { .user = "core" };
const char** extras = NULL;
int extra_count = 0;
const char *err = NULL;
diff --git a/src/ssb.export.c b/src/ssb.export.c
index 00379dbca..d9986c197 100644
--- a/src/ssb.export.c
+++ b/src/ssb.export.c
@@ -9,7 +9,7 @@
#include
#include
-static void _write_file(const char* path, void* blob, size_t size)
+static void _write_file(const char* path, const void* blob, size_t size)
{
FILE* file = fopen(path, "wb");
if (file)
@@ -35,6 +35,45 @@ static void _make_dir(const char* path)
}
}
+typedef struct _tf_export_t
+{
+ tf_ssb_t* ssb;
+ const char* parent;
+ JSValue files;
+ uv_fs_t req;
+ bool done;
+} tf_export_t;
+
+static void _tf_ssb_export_scandir(uv_fs_t* req)
+{
+ tf_export_t* export = req->data;
+ JSContext* context = tf_ssb_get_context(export->ssb);
+ uv_dirent_t ent;
+ while (uv_fs_scandir_next(req, &ent) == 0)
+ {
+ if (ent.type == UV_DIRENT_FILE)
+ {
+ JSValue found = JS_GetPropertyStr(context, export->files, ent.name);
+ if (JS_IsUndefined(found))
+ {
+ size_t len = strlen(export->parent) + strlen(ent.name) + 2;
+ char* path = tf_malloc(len);
+ snprintf(path, len, "%s/%s", export->parent, ent.name);
+ uv_fs_t req = { 0 };
+ int r = uv_fs_unlink(tf_ssb_get_loop(export->ssb), &req, path, NULL);
+ if (r)
+ {
+ printf("Failed to unlink %s: %s.", path, uv_strerror(r));
+ }
+ uv_fs_req_cleanup(&req);
+ tf_free(path);
+ }
+ JS_FreeValue(context, found);
+ }
+ }
+ export->done = true;
+}
+
void tf_ssb_export(tf_ssb_t* ssb, const char* key)
{
char user[256] = { 0 };
@@ -80,12 +119,9 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
}
char file_path[1024];
_make_dir("apps/");
- snprintf(file_path, sizeof(file_path), "apps/%s", user);
+ snprintf(file_path, sizeof(file_path), "apps/%s", path);
_make_dir(file_path);
- snprintf(file_path, sizeof(file_path), "apps/%s/%s", user, path);
- _make_dir(file_path);
- snprintf(file_path, sizeof(file_path), "apps/%s/%s.json", user, path);
- _write_file(file_path, blob, size);
+ snprintf(file_path, sizeof(file_path), "apps/%s.json", path);
JSContext* context = tf_ssb_get_context(ssb);
JSValue app = JS_ParseJSON(context, (const char*)blob, size, NULL);
tf_free(blob);
@@ -108,7 +144,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
size_t file_size = 0;
if (tf_ssb_db_blob_get(ssb, blob_id, &file_blob, &file_size))
{
- snprintf(file_path, sizeof(file_path), "apps/%s/%s/%s", user, path, file_name);
+ snprintf(file_path, sizeof(file_path), "apps/%s/%s", path, file_name);
_write_file(file_path, file_blob, file_size);
tf_free(file_blob);
}
@@ -129,6 +165,40 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
}
js_free(context, ptab);
+ JSAtom files_atom = JS_NewAtom(context, "files");
+ JS_DeleteProperty(context, app, files_atom, 0);
+ JS_FreeAtom(context, files_atom);
+
+ JSValue json = JS_JSONStringify(context, app, JS_NULL, JS_NewInt32(context, 2));
+ size_t length = 0;
+ const char* string = JS_ToCStringLen(context, &length, json);
+ snprintf(file_path, sizeof(file_path), "apps/%s.json", path);
+ _write_file(file_path, string, length);
+ JS_FreeCString(context, string);
+ JS_FreeValue(context, json);
+
+ snprintf(file_path, sizeof(file_path), "apps//%s", path);
+ tf_export_t export =
+ {
+ .parent = file_path,
+ .ssb = ssb,
+ .files = files,
+ .req =
+ {
+ .data = &export,
+ },
+ };
+ int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &export.req, file_path, 0, _tf_ssb_export_scandir);
+ if (r)
+ {
+ printf("Failed to scan directory %s: %s.", file_path, uv_strerror(r));
+ }
+ while (!export.done)
+ {
+ uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE);
+ }
+ uv_fs_req_cleanup(&export.req);
+
JS_FreeValue(context, files);
JS_FreeValue(context, app);
}
diff --git a/src/ssb.import.c b/src/ssb.import.c
index 6cfb9e3e5..657f515c0 100644
--- a/src/ssb.import.c
+++ b/src/ssb.import.c
@@ -12,25 +12,6 @@
#include
#include
-typedef struct _tf_import_file_t {
- uv_fs_t req;
- uv_file file;
- tf_ssb_t* ssb;
- const char* user;
- const char* parent;
- const char* name;
- char data[k_ssb_blob_bytes_max];
- int* work_left;
-} tf_import_file_t;
-
-static void _tf_ssb_import_file_close(uv_fs_t* req)
-{
- tf_import_file_t* file = req->data;
- (*file->work_left)--;
- uv_fs_req_cleanup(req);
- tf_free(req->data);
-}
-
static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char* app)
{
sqlite3_stmt* statement;
@@ -80,6 +61,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
JS_FreeCString(context, last_added);
last_added = read_string;
}
+ JS_FreeCString(context, last_added);
JSValue json = JS_JSONStringify(context, out_apps, JS_NULL, JS_NULL);
const char* text = JS_ToCString(context, json);
@@ -99,6 +81,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
JS_FreeValue(context, apps);
}
+/*
static void _tf_ssb_import_file_read(uv_fs_t* req)
{
tf_import_file_t* file = req->data;
@@ -106,32 +89,49 @@ static void _tf_ssb_import_file_read(uv_fs_t* req)
if (req->result >= 0)
{
bool is_new = false;
- if (tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id), &is_new))
+ bool is_app_json = false;
+
+ if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0)
{
- if (is_new)
+ JSContext* context = tf_ssb_get_context(file->ssb);
+ JSValue object = JS_ParseJSON(context, file->data, req->result, NULL);
+ JSValue type = JS_GetPropertyStr(context, object, "type");
+ if (!JS_IsUndefined(type))
{
- printf("Stored %s/%s as %s.\n", file->parent, file->name, id);
- }
- if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0)
- {
- sqlite3_stmt* statement;
- if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
+ const char* type_string = JS_ToCString(context, type);
+ if (type_string && strcmp(type_string, "tf-application") == 0)
{
- ((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0';
- if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK &&
- sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK &&
- sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
- sqlite3_step(statement) == SQLITE_DONE)
+ is_app_json = true;
+
+ sqlite3_stmt* statement;
+ if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
{
- if (sqlite3_changes(tf_ssb_get_db(file->ssb)))
+ ((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0';
+ if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK &&
+ sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK &&
+ sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
+ sqlite3_step(statement) == SQLITE_DONE)
{
- printf("Registered %s path:%s as %s.\n", file->user, file->name, id);
+ if (sqlite3_changes(tf_ssb_get_db(file->ssb)))
+ {
+ printf("Registered %s path:%s as %s.\n", file->user, file->name, id);
+ }
+ _tf_ssb_import_add_app(file->ssb, file->user, file->name);
}
- _tf_ssb_import_add_app(file->ssb, file->user, file->name);
+ sqlite3_finalize(statement);
}
- sqlite3_finalize(statement);
}
+ JS_FreeCString(context, type_string);
}
+ JS_FreeValue(context, object);
+
+ }
+
+ if (!is_app_json &&
+ tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id), &is_new) &&
+ is_new)
+ {
+ printf("Stored %s/%s as %s.\n", file->parent, file->name, id);
}
}
uv_fs_req_cleanup(req);
@@ -146,74 +146,178 @@ static void _tf_ssb_import_file_open(uv_fs_t* req)
uv_fs_read(tf_ssb_get_loop(file->ssb), req, file->file, &(uv_buf_t) { .base = file->data, .len = k_ssb_blob_bytes_max }, 1, 0, _tf_ssb_import_file_read);
}
-typedef struct _tf_import_t {
+static void _tf_ssb_import_register_app(tf_ssb_t* ssb, const char* use, const char* app, const char* id)
+{
+ sqlite3_stmt* statement;
+ if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
+ {
+ ((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0';
+ if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK &&
+ sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK &&
+ sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
+ sqlite3_step(statement) == SQLITE_DONE)
+ {
+ if (sqlite3_changes(tf_ssb_get_db(file->ssb)))
+ {
+ printf("Registered %s path:%s as %s.\n", file->user, file->name, id);
+ }
+ _tf_ssb_import_add_app(file->ssb, file->user, file->name);
+ }
+ sqlite3_finalize(statement);
+ }
+}
+*/
+
+typedef struct _tf_import_t
+{
tf_ssb_t* ssb;
const char* user;
const char* parent;
uv_fs_t req;
- int work_left;
} tf_import_t;
-static void _tf_ssb_import_scandir(uv_fs_t* req)
+static char* _tf_ssb_import_read_file(uv_loop_t* loop, const char* path, size_t* out_size)
{
- tf_import_t* import = req->data;
- uv_dirent_t ent;
- while (uv_fs_scandir_next(req, &ent) == 0)
+ char* data = NULL;
+ uv_fs_t req = { 0 };
+ int handle = uv_fs_open(loop, &req, path, 0, 0, NULL);
+ if (handle >= 0)
{
- size_t len = strlen(import->parent) + strlen(ent.name) + 2;
- char* path = tf_malloc(len);
- snprintf(path, len, "%s/%s", import->parent, ent.name);
- if (ent.type == UV_DIRENT_DIR)
+ uv_fs_t read_req = { 0 };
+ data = tf_malloc(k_ssb_blob_bytes_max);
+ int r = uv_fs_read(loop, &read_req, handle, &(uv_buf_t) { .base = data, .len = k_ssb_blob_bytes_max }, 1, 0, NULL);
+ if (r >= 0 && r < k_ssb_blob_bytes_max)
{
- tf_ssb_import(import->ssb, import->user, path);
+ data[r] = '\0';
+ *out_size = r;
}
else
{
- size_t size = sizeof(tf_import_file_t) + strlen(import->parent) +1 + strlen(ent.name) + 1;
- tf_import_file_t* file = tf_malloc(size);
- memset(file, 0, size);
- file->ssb = import->ssb;
- file->user = import->user;
- file->parent = (void*)(file + 1);
- file->name = file->parent + strlen(import->parent) + 1;
- file->req.data = file;
- file->work_left = &import->work_left;
- memcpy((char*)file->parent, import->parent, strlen(import->parent) + 1);
- memcpy((char*)file->name, ent.name, strlen(ent.name) + 1);
+ printf("Failed to read %s: %s.\n", path, uv_strerror(r));
+ }
+ uv_fs_req_cleanup(&read_req);
- import->work_left++;
- int r = uv_fs_open(tf_ssb_get_loop(import->ssb), &file->req, path, 0, 0, _tf_ssb_import_file_open);
- if (r < 0)
+ uv_fs_t close_req = { 0 };
+ r = uv_fs_close(loop, &close_req, handle, NULL);
+ if (r)
+ {
+ printf("Failed to close %s: %s.\n", path, uv_strerror(r));
+ }
+ }
+ else
+ {
+ printf("Failed to open %s: %s.\n", path, uv_strerror(handle));
+ }
+ uv_fs_req_cleanup(&req);
+ return data;
+}
+
+static void _tf_ssb_import_recursive_add_files(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* context, JSValue files, const char* root, const char* path)
+{
+ uv_fs_t req = { 0 };
+ int r = uv_fs_scandir(loop, &req, path, 0, NULL);
+ if (r >= 0)
+ {
+ uv_dirent_t ent;
+ while (uv_fs_scandir_next(&req, &ent) == 0)
+ {
+ if (ent.type == UV_DIRENT_FILE)
{
- printf("Failed to open %s: %s.\n", path, uv_strerror(r));
- tf_free(file);
- import->work_left--;
+ size_t len = strlen(path) + strlen(ent.name) + 2;
+ char* full_path = tf_malloc(len);
+ snprintf(full_path, len, "%s/%s", path, ent.name);
+
+ size_t size = 0;
+ char* blob = _tf_ssb_import_read_file(loop, full_path, &size);
+ char id[k_id_base64_len] = { 0 };
+ bool is_new = false;
+ if (tf_ssb_db_blob_store(ssb, (const uint8_t*)blob, size, id, sizeof(id), &is_new) && is_new)
+ {
+ printf("Stored %s as %s.\n", full_path, id);
+ }
+ JS_SetPropertyStr(context, files, full_path + strlen(root) + 1, JS_NewString(context, id));
+
+ tf_free(blob);
+ tf_free(full_path);
}
}
- tf_free(path);
}
- import->work_left--;
+ else
+ {
+ printf("Failed to scan directory %s: %s.", path, uv_strerror(r));
+ }
+ uv_fs_req_cleanup(&req);
+}
+
+static void _tf_ssb_import_app_json(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* context, const char* user, const char* path)
+{
+ uv_fs_t req = { 0 };
+ int r = uv_fs_open(loop, &req, path, 0, 0, NULL);
+ if (r >= 0)
+ {
+ size_t size = 0;
+ char* file = _tf_ssb_import_read_file(loop, path, &size);
+ if (file)
+ {
+ JSValue app = JS_ParseJSON(context, file, size, NULL);
+ if (!tf_util_report_error(context, app))
+ {
+ char* dir = tf_strdup(path);
+ dir[strlen(dir) - strlen(".json")] = '\0';
+ JSValue files = JS_NewObject(context);
+ _tf_ssb_import_recursive_add_files(ssb, loop, context, files, dir, dir);
+ JS_SetPropertyStr(context, app, "files", files);
+
+ JSValue json = JS_JSONStringify(context, app, JS_NULL, JS_NULL);
+ size_t size = 0;
+ const char* blob = JS_ToCStringLen(context, &size, json);
+ char id[k_id_base64_len] = { 0 };
+ if (tf_ssb_db_blob_store(ssb, (const uint8_t*)blob, size, id, sizeof(id), NULL))
+ {
+ const char* app = dir;
+ char* slash = strrchr(dir, '/');
+ app = slash ? slash + 1 : app;
+ _tf_ssb_import_add_app(ssb, user, app);
+ }
+ JS_FreeCString(context, blob);
+ JS_FreeValue(context, json);
+
+ tf_free(dir);
+ }
+ JS_FreeValue(context, app);
+ tf_free(file);
+ }
+ }
+ else
+ {
+ printf("Failed to open %s: %s.\n", path, uv_strerror(r));
+ }
+ uv_fs_req_cleanup(&req);
}
void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path)
{
- tf_import_t import = {
- .ssb = ssb,
- .user = user,
- .parent = path,
- .work_left = 1,
- };
- import.req.data = &import;
+ uv_fs_t req = { 0 };
sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000);
- int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &import.req, path, 0, _tf_ssb_import_scandir);
- if (r)
+ int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &req, path, 0, NULL);
+ if (r >= 0)
+ {
+ uv_dirent_t ent;
+ while (uv_fs_scandir_next(&req, &ent) == 0)
+ {
+ size_t len = strlen(path) + strlen(ent.name) + 2;
+ char* full_path = tf_malloc(len);
+ snprintf(full_path, len, "%s/%s", path, ent.name);
+ if (strcasecmp(ent.name + strlen(ent.name) - strlen(".json"), ".json") == 0)
+ {
+ _tf_ssb_import_app_json(ssb, tf_ssb_get_loop(ssb), tf_ssb_get_context(ssb), user, full_path);
+ }
+ tf_free(full_path);
+ }
+ }
+ else
{
printf("Failed to scan directory %s: %s.", path, uv_strerror(r));
}
-
- while (import.work_left > 0)
- {
- uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE);
- }
- uv_fs_req_cleanup(&import.req);
+ uv_fs_req_cleanup(&req);
}