Replace folder sync with an HTTP sync server, keeping the app offline-first
Folder sync left too much to the user's sync tool: platform file access
(the Android SAF plugin), choosing the document, merging conflict copies —
and its merge base could fall out of step with the document (absorbing
conflict copies, "forget", picking a new Android folder), reading every
difference as a deletion.
The new mtgc-server crate stores one versioned library per user and accepts
a write only when it names the version it replaces. It never merges: devices
still do that with merge3, through a new sync cycle over a `Remote` trait in
mtgc-core. The base advances only on knowledge — to what was read, or to a
write the server confirmed — and is stored with the server and account it
belongs to, so a different account starts from an empty ancestor instead of
deleting. The working copy is only rewritten under the app-state lock, and
sync commands now run off the main thread.
Sign-in happens in the browser on the server's pages, via the owner's OIDC
provider and/or local username/password accounts (Argon2id, managed with
`mtgc-server user ...`), and returns a per-device revocable token to the app
by PKCE-protected code: a loopback port on desktop, the mtgc:// scheme on
Android.
The Info tab gains sign-in, the first-sync combine/replace choice, the
account's devices, and JSON export/import; the top bar shows sync state.
Includes a Dockerfile, Compose/Caddy example, and scripts to build and push
${DOCKER_REGISTRY}/mtgc/sync-server.
This commit is contained in:
68
scripts/sync-server-image.sh
Executable file
68
scripts/sync-server-image.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# sync-server-image.sh — build or push the mtgc sync server's container image.
|
||||
#
|
||||
# The image is tagged ${DOCKER_REGISTRY}/mtgc/sync-server:${IMAGE_TAG}.
|
||||
#
|
||||
# Usage:
|
||||
# DOCKER_REGISTRY=registry.example.com ./scripts/sync-server-image.sh build
|
||||
# DOCKER_REGISTRY=registry.example.com ./scripts/sync-server-image.sh push
|
||||
#
|
||||
# Or through npm: `npm run sync-server:build` / `npm run sync-server:push`.
|
||||
#
|
||||
# Environment:
|
||||
# DOCKER_REGISTRY (required) registry host, optionally with a namespace path
|
||||
# IMAGE_TAG tag to build/push (default: latest)
|
||||
# CONTAINER_ENGINE docker or podman (default: docker if installed, else podman)
|
||||
#
|
||||
# `push` pushes what `build` produced; it doesn't rebuild. Log in to the
|
||||
# registry first (`docker login "$DOCKER_REGISTRY"`).
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
sed -n '3,20p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit "${1:-0}"
|
||||
}
|
||||
|
||||
[ $# -eq 1 ] || usage 1
|
||||
case "$1" in
|
||||
build | push) action="$1" ;;
|
||||
-h | --help) usage ;;
|
||||
*) usage 1 ;;
|
||||
esac
|
||||
|
||||
if [ -z "${DOCKER_REGISTRY:-}" ]; then
|
||||
echo "sync-server-image: set DOCKER_REGISTRY (e.g. registry.example.com)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
engine="${CONTAINER_ENGINE:-}"
|
||||
if [ -z "$engine" ]; then
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
engine=docker
|
||||
elif command -v podman >/dev/null 2>&1; then
|
||||
engine=podman
|
||||
else
|
||||
echo "sync-server-image: neither docker nor podman is installed" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
image="${DOCKER_REGISTRY%/}/mtgc/sync-server:${IMAGE_TAG:-latest}"
|
||||
|
||||
# The Dockerfile builds from the workspace root, so run from there whatever
|
||||
# directory this was invoked in.
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
case "$action" in
|
||||
build)
|
||||
echo "==> building $image"
|
||||
"$engine" build -f crates/mtgc-server/Dockerfile -t "$image" .
|
||||
;;
|
||||
push)
|
||||
echo "==> pushing $image"
|
||||
"$engine" push "$image"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user