Initial SyncGames tree: agent, Android, deploy, docs.
Session-gated MinIO save sync with AppImage GUI, CLI edit/session flow, and Gitea release helper. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# MinIO root (Console login uses THESE — exact match)
|
||||
MINIO_ROOT_USER=syncgamesadmin
|
||||
MINIO_ROOT_PASSWORD=change-me-root-password-32chars
|
||||
|
||||
# === Console WebUI (required for login from your PC) ===
|
||||
# Replace 192.168.1.50 with your NAS LAN IP (same host you put in the browser).
|
||||
# After up, logs MUST show this IP — NOT example.com and NOT min.hisora.dev.
|
||||
MINIO_SERVER_URL=http://192.168.1.50:9000
|
||||
MINIO_BROWSER_REDIRECT_URL=http://192.168.1.50:9001
|
||||
|
||||
# SyncGames agents use Cloudflare HTTPS in agent.toml (separate from above).
|
||||
SYNCGAMES_BUCKET=syncgames
|
||||
APP_ACCESS_KEY=syncgamesagent
|
||||
APP_SECRET_KEY=change-me-agent-secret-32chars
|
||||
@@ -0,0 +1,113 @@
|
||||
# Docker Compose — SyncGames MinIO
|
||||
|
||||
Deploys the Path 3 SSOT store on your NAS. Keep using your existing NGINX + Cloudflare Tunnel in front.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
cd SyncGames/deploy/docker # or /volume1/docker/minio on Synology
|
||||
cp .env.example .env
|
||||
# edit .env — passwords, MINIO_SERVER_URL, APP_* keys
|
||||
# IMPORTANT: save as UTF-8, Unix (LF) line endings, no BOM
|
||||
|
||||
docker compose up -d
|
||||
docker compose --profile init run --rm createbuckets
|
||||
```
|
||||
|
||||
### Synology / `.env` encoding errors (`\x00` in variable name)
|
||||
|
||||
Docker Compose only accepts **UTF-8** `.env` files. Editing in Windows Notepad, WordPad, or some Synology File Station flows saves **UTF-16**, which shows up as:
|
||||
|
||||
`unexpected character "\x00" in variable name`
|
||||
|
||||
**Fix on the NAS** (SSH):
|
||||
|
||||
```bash
|
||||
cd /volume1/docker/minio
|
||||
# inspect (lots of 00 = UTF-16)
|
||||
od -An -tx1 .env | head
|
||||
|
||||
# recreate clean UTF-8 (overwrite after backing up your secrets)
|
||||
mv .env .env.bak.utf16 2>/dev/null || true
|
||||
cat > .env <<'EOF'
|
||||
MINIO_ROOT_USER=syncgamesadmin
|
||||
MINIO_ROOT_PASSWORD=change-me-root-password-32chars
|
||||
MINIO_SERVER_URL=https://syncgames-s3.example.com
|
||||
SYNCGAMES_BUCKET=syncgames
|
||||
APP_ACCESS_KEY=syncgamesagent
|
||||
APP_SECRET_KEY=change-me-agent-secret-32chars
|
||||
EOF
|
||||
|
||||
# or convert if the text is still readable:
|
||||
# iconv -f UTF-16 -t UTF-8 .env.bak.utf16 | tr -d '\r' > .env
|
||||
|
||||
file .env # should say: UTF-8 text (or ASCII)
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Prefer editing `.env` over SSH (`nano`/`vi`) or an editor set to **UTF-8 / LF**. Avoid Notepad’s default Unicode save.
|
||||
|
||||
MinIO listens on **127.0.0.1:9000** (API) and **127.0.0.1:9001** (console).
|
||||
|
||||
## Wire to existing NGINX
|
||||
|
||||
Copy or include [`nginx-syncgames-s3.conf`](nginx-syncgames-s3.conf), set `server_name` to your Cloudflare hostname, reload NGINX.
|
||||
|
||||
Point Cloudflare Tunnel at that NGINX vhost (same pattern as your other services).
|
||||
|
||||
## Agent config
|
||||
|
||||
```toml
|
||||
endpoint_url = "https://syncgames-s3.example.com"
|
||||
bucket = "syncgames"
|
||||
region = "us-east-1"
|
||||
path_style = true
|
||||
access_key = "<APP_ACCESS_KEY from .env>"
|
||||
secret_key = "<APP_SECRET_KEY from .env>"
|
||||
```
|
||||
|
||||
Then run `syncgames doctor` (or Doctor in the AppImage GUI).
|
||||
|
||||
## Optional tunnel profile
|
||||
|
||||
Only if you do **not** already terminate tunnels on the NAS:
|
||||
|
||||
```bash
|
||||
# set CLOUDFLARE_TUNNEL_TOKEN in .env
|
||||
docker compose --profile tunnel up -d
|
||||
```
|
||||
|
||||
Configure the tunnel hostname to `http://127.0.0.1:80` (NGINX) ideally, or `http://127.0.0.1:9000` for direct MinIO (skips NGINX hardening — not preferred).
|
||||
|
||||
## Data
|
||||
|
||||
Volume: `syncgames_minio_data`. Console: http://127.0.0.1:9001 (or NAS LAN IP if you publish the port) with **root** user/password from `.env`.
|
||||
|
||||
### Console “unable to login due to network error”
|
||||
|
||||
MinIO Console runs in your **browser**. Login XHRs must reach the **API URL**, which must be a host your PC can open.
|
||||
|
||||
Your logs were advertising `API: https://syncgames-s3.example.com` / `https://min.hisora.dev` — the browser cannot complete Console login against those from `:9001`.
|
||||
|
||||
**Working LAN setup**
|
||||
|
||||
1. Copy updated `docker-compose.yml` to the NAS.
|
||||
2. In `.env` set (use your real NAS IP):
|
||||
|
||||
```bash
|
||||
MINIO_SERVER_URL=http://192.168.1.50:9000
|
||||
MINIO_BROWSER_REDIRECT_URL=http://192.168.1.50:9001
|
||||
```
|
||||
|
||||
3. Recreate:
|
||||
|
||||
```bash
|
||||
docker compose up -d --force-recreate minio
|
||||
docker compose logs minio | head -40
|
||||
```
|
||||
|
||||
4. Confirm logs show `API: http://192.168.1.50:9000` — **not** `example.com` or `min.hisora.dev`.
|
||||
5. Open **exactly** `http://192.168.1.50:9001` (same IP).
|
||||
6. Login as `MINIO_ROOT_USER` (exact spelling).
|
||||
|
||||
Cloudflare/`https://min.hisora.dev` is for SyncGames agents in `agent.toml` only — not for Console env vars.
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Run this FROM YOUR PC (not inside the NAS container) while debugging Console login.
|
||||
# Usage: ./check-console.sh 192.168.1.5
|
||||
|
||||
HOST="${1:-192.168.1.5}"
|
||||
|
||||
echo "==> API health http://${HOST}:9000/minio/health/live"
|
||||
if curl -fsS --connect-timeout 3 "http://${HOST}:9000/minio/health/live"; then
|
||||
echo
|
||||
echo "OK — port 9000 reachable"
|
||||
else
|
||||
echo
|
||||
echo "FAIL — browser Console cannot log in if API :9000 is blocked (Synology Firewall / port publish)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "==> Console http://${HOST}:9001/"
|
||||
if curl -fsS -o /dev/null --connect-timeout 3 "http://${HOST}:9001/"; then
|
||||
echo "OK — port 9001 reachable"
|
||||
else
|
||||
echo "FAIL — cannot load WebUI"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Open EXACTLY: http://${HOST}:9001"
|
||||
echo "Login as MINIO_ROOT_USER from .env (e.g. jorg) — not app access key."
|
||||
@@ -0,0 +1,17 @@
|
||||
# Synology / LAN WebUI fix — use host networking so Console+API bind on the NAS IP.
|
||||
# Usage ON THE NAS:
|
||||
# docker compose -f docker-compose.yml -f docker-compose.host.yml up -d --force-recreate minio
|
||||
#
|
||||
# Then open exactly: http://192.168.1.5:9001
|
||||
# (same IP as MINIO_SERVER_URL / MINIO_BROWSER_REDIRECT_URL in .env)
|
||||
|
||||
services:
|
||||
minio:
|
||||
network_mode: host
|
||||
# host mode ignores "ports:" and "networks:" — bind on NAS interfaces directly
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
MINIO_SERVER_URL: ${MINIO_SERVER_URL}
|
||||
MINIO_BROWSER_REDIRECT_URL: ${MINIO_BROWSER_REDIRECT_URL}
|
||||
# keep command from base compose; host mode serves :9000 and :9001 on the NAS
|
||||
@@ -0,0 +1,93 @@
|
||||
# SyncGames SSOT — MinIO
|
||||
#
|
||||
# Console login rule: the URL in the browser address bar and MINIO_*_URL
|
||||
# must both be reachable FROM YOUR PC'S BROWSER (not only from Docker).
|
||||
#
|
||||
# cp .env.example .env # set passwords + NAS_LAN_IP
|
||||
# docker compose up -d
|
||||
# docker compose --profile init run --rm createbuckets
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: syncgames-minio
|
||||
restart: unless-stopped
|
||||
command: server /data --console-address ":9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
# Must be URLs your BROWSER can open (NAS LAN IP or hostname).
|
||||
# Do NOT set these to the Cloudflare public HTTPS hostname for Console use.
|
||||
MINIO_SERVER_URL: ${MINIO_SERVER_URL}
|
||||
MINIO_BROWSER_REDIRECT_URL: ${MINIO_BROWSER_REDIRECT_URL}
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
ports:
|
||||
# 0.0.0.0 so PCs on LAN can reach API+Console (needed for WebUI login)
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
networks:
|
||||
- syncgames
|
||||
|
||||
createbuckets:
|
||||
image: quay.io/minio/mc:RELEASE.2025-04-16T18-13-26Z
|
||||
container_name: syncgames-mc-init
|
||||
profiles: ["init"]
|
||||
depends_on:
|
||||
- minio
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
SYNCGAMES_BUCKET: ${SYNCGAMES_BUCKET:-syncgames}
|
||||
APP_ACCESS_KEY: ${APP_ACCESS_KEY}
|
||||
APP_SECRET_KEY: ${APP_SECRET_KEY}
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
echo "Waiting for MinIO..."
|
||||
i=0
|
||||
until mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD" 2>/dev/null; do
|
||||
i=$$((i+1))
|
||||
if [ "$$i" -gt 30 ]; then echo "MinIO not ready"; exit 1; fi
|
||||
sleep 2
|
||||
done
|
||||
mc mb --ignore-existing "local/$${SYNCGAMES_BUCKET}"
|
||||
mc anonymous set none "local/$${SYNCGAMES_BUCKET}"
|
||||
# App user for agents — fail loudly if key cannot be created
|
||||
if ! mc admin user info local "$$APP_ACCESS_KEY" >/dev/null 2>&1; then
|
||||
mc admin user add local "$$APP_ACCESS_KEY" "$$APP_SECRET_KEY"
|
||||
else
|
||||
mc admin user add local "$$APP_ACCESS_KEY" "$$APP_SECRET_KEY" 2>/dev/null || true
|
||||
# update secret if user exists (MinIO: remove+readd or policy only)
|
||||
echo "User $$APP_ACCESS_KEY already exists"
|
||||
fi
|
||||
mc admin policy attach local readwrite --user "$$APP_ACCESS_KEY"
|
||||
mc admin user info local "$$APP_ACCESS_KEY"
|
||||
echo "Bucket $${SYNCGAMES_BUCKET} ready"
|
||||
mc ls local
|
||||
|
||||
cloudflared:
|
||||
image: cloudflare/cloudflared:latest
|
||||
container_name: syncgames-cloudflared
|
||||
profiles: ["tunnel"]
|
||||
restart: unless-stopped
|
||||
command: tunnel --no-autoupdate run
|
||||
environment:
|
||||
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}
|
||||
network_mode: host
|
||||
|
||||
networks:
|
||||
syncgames:
|
||||
name: syncgames
|
||||
|
||||
volumes:
|
||||
minio_data:
|
||||
name: syncgames_minio_data
|
||||
@@ -0,0 +1,42 @@
|
||||
# SyncGames MinIO — reverse proxy snippet for an EXISTING host NGINX.
|
||||
# Include from your main nginx.conf or drop into conf.d/.
|
||||
#
|
||||
# Cloudflare Tunnel should target this host's NGINX (port 80 / 443), not MinIO directly.
|
||||
# Docker Compose binds MinIO to 127.0.0.1:9000 on the NAS.
|
||||
|
||||
upstream syncgames_minio {
|
||||
server 127.0.0.1:9000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name syncgames-s3.example.com; # <-- your Cloudflare hostname
|
||||
|
||||
client_max_body_size 512m;
|
||||
ignore_invalid_headers off;
|
||||
|
||||
location / {
|
||||
proxy_pass http://syncgames_minio;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Critical for SigV4 — do not rewrite Host to localhost
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header Content-Type $content_type;
|
||||
proxy_set_header Content-Length $content_length;
|
||||
proxy_set_header Expect $http_expect;
|
||||
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# SyncGames S3 API — NGINX example
|
||||
# Replace syncgames-s3.example.com and upstream as needed.
|
||||
# Place behind Cloudflare Tunnel; do NOT put auth_basic on this vhost.
|
||||
|
||||
upstream syncgames_minio {
|
||||
server 127.0.0.1:9000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name syncgames-s3.example.com;
|
||||
|
||||
# Emulator dumps / large co-op packs
|
||||
client_max_body_size 512m;
|
||||
|
||||
# S3 clients send unusual headers
|
||||
ignore_invalid_headers off;
|
||||
|
||||
location / {
|
||||
proxy_pass http://syncgames_minio;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Critical for SigV4 — do not rewrite to localhost
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header Content-Type $content_type;
|
||||
proxy_set_header Content-Length $content_length;
|
||||
proxy_set_header Expect $http_expect;
|
||||
|
||||
# Avoid truncated PUTs
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user