This commit is contained in:
team3
2026-09-22 13:28:52 +02:00
parent 2472e89b9c
commit acfe29dae7
4 changed files with 50 additions and 12 deletions

View File

@@ -30,6 +30,14 @@ const FILES_INTERVAL_SECONDS = 10 * 60;
const FILES_IDLE_SECONDS = 60 * 60;
const FILES_BATCH_BYTES = 16 * 1024 * 1024;
// Von außen anstoßbar: make sync-files ruft diese Methode über D-Bus auf
const DBUS_IFACE = `
<node>
<interface name="de.marha.Desktop">
<method name="SyncFiles"><arg type="s" direction="out" name="summary"/></method>
</interface>
</node>`;
export class Sync {
constructor(store) {
this._store = store;
@@ -70,6 +78,20 @@ export class Sync {
this._scheduleNext();
if (this._filesRoot)
this._scheduleFiles(FILES_FIRST_SECONDS);
this._dbus = Gio.DBusExportedObject.wrapJSObject(DBUS_IFACE, this);
this._dbus.export(Gio.DBus.session, '/de/marha/desktop');
}
// Dateiabgleich sofort, die Antwort kommt erst, wenn er durch ist
SyncFilesAsync(_params, invocation) {
const reply = text => invocation.return_value(new GLib.Variant('(s)', [text]));
if (!this._filesRoot) {
reply('Dateiabgleich ist aus ("files": false in sync.json)');
return;
}
this._filesCycle()
.then(r => reply(r ? `hoch ${r.uploaded}, gelöscht gemeldet ${r.removed}, runter ${r.written}, lokal gelöscht ${r.deleted}` : 'läuft gerade schon'))
.catch(e => reply(`Fehler: ${e.message}`));
}
// Aktueller Takt in Sekunden, je nachdem wie lange die letzte Aktivität her ist.
@@ -109,6 +131,8 @@ export class Sync {
}
stop() {
this._dbus?.unexport();
this._dbus = null;
if (this._timerId) {
GLib.source_remove(this._timerId);
this._timerId = null;
@@ -250,10 +274,12 @@ export class Sync {
}
// Eigener Lauf mit eigenem Stand: die Antwort enthält nur Dateien, der Takt der Einträge bleibt unberührt.
// Gibt zurück, was übertragen wurde, oder null, wenn gerade ein Lauf aktiv ist.
async _filesCycle() {
if (this._filesBusy || !this._filesRoot)
return;
return null;
this._filesBusy = true;
const result = {uploaded: 0, removed: 0, written: 0, deleted: 0};
try {
const state = this._state.files;
const local = scanFiles(this._filesRoot);
@@ -275,17 +301,20 @@ export class Sync {
// Ein Server ohne Dateien-Sammlung würde alles verwerfen, der Stand bliebe sonst falsch
if (!Array.isArray(response.files))
throw new Error('Server kennt keine Dateien, dort erst make prod ausführen');
for (const item of files)
for (const item of files) {
state.pushed[item.id] = item.deleted ? null : item.updated;
this._applyFiles(response, local);
result[item.deleted ? 'removed' : 'uploaded']++;
}
this._applyFiles(response, local, result);
writeJson(this._statePath, this._state);
}
} finally {
this._filesBusy = false;
}
return result;
}
_applyFiles(response, local) {
_applyFiles(response, local, result) {
const state = this._state.files;
for (const {file, action, mine} of planPull(response.files ?? [], local)) {
const path = GLib.build_filenamev([this._filesRoot, file.id]);
@@ -296,10 +325,12 @@ export class Sync {
Gio.File.new_for_path(path).set_attribute_uint64('time::modified', seconds, Gio.FileQueryInfoFlags.NONE, null);
local.set(file.id, seconds);
state.pushed[file.id] = file.updated;
result.written++;
} else if (action === 'delete') {
GLib.unlink(path);
local.delete(file.id);
state.pushed[file.id] = null;
result.deleted++;
} else if (file.deleted && mine === null) {
state.pushed[file.id] = null;
} else if (mine === file.updated) {