|
|
|
|
SHELL := bash
|
|
|
|
|
.SHELLFLAGS := -eu -o pipefail -c
|
|
|
|
|
|
|
|
|
|
.DEFAULT_GOAL := help
|
|
|
|
|
|
|
|
|
|
COMPOSE ?= docker compose
|
|
|
|
|
PROD_COMPOSE_FILE ?= docker-compose.yml
|
|
|
|
|
COMPOSE_PROD := $(COMPOSE) -f $(PROD_COMPOSE_FILE)
|
|
|
|
|
COMPOSE_ENV_FILES := .env.postgres .env.redis .env.synctv
|
feat(web): add client-aware playback and reproducible UI (#433)
## Summary
- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous
## Reproducible Web UI
- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes
## Verification
- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows
Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52
Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
|
|
|
CARGO ?= cargo
|
|
|
|
|
CROSS ?= cargo cross
|
|
|
|
|
CARGO_LOCKED ?= --locked
|
|
|
|
|
CARGO_WORKSPACE_ARGS ?= --workspace
|
|
|
|
|
CARGO_ALL_TARGETS_ARGS ?= --all-targets
|
|
|
|
|
DEV_COMPOSE_FILE ?= docker-compose.dev.yml
|
|
|
|
|
DEV_PROJECT ?= synctv-dev
|
|
|
|
|
DEV_BASE_SERVICES ?= postgres redis
|
|
|
|
|
DEV_OPTIONAL_SERVICES ?= rustfs openlist nextcloud seafile seafile-db seafile-memcached emby jellyfin casdoor
|
|
|
|
|
DEV_STACK_SERVICES ?= $(DEV_BASE_SERVICES) $(DEV_OPTIONAL_SERVICES)
|
|
|
|
|
DEV_STACK_WAIT_SERVICES ?= $(DEV_STACK_SERVICES) rustfs-init openlist-init nextcloud-init seafile-init emby-init jellyfin-init
|
|
|
|
|
DEV_WAIT_TIMEOUT ?= 120
|
|
|
|
|
DEV_LOG_TAIL ?= 100
|
|
|
|
|
CPU_COUNT ?= $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 1)
|
|
|
|
|
DEV_JOBS ?= $(CPU_COUNT)
|
|
|
|
|
CARGO_JOBS_ARGS ?= -j "$(DEV_JOBS)"
|
|
|
|
|
CARGO_BUILD_ARGS ?= $(CARGO_JOBS_ARGS) $(CARGO_LOCKED)
|
|
|
|
|
CARGO_WORKSPACE_BUILD_ARGS ?= $(CARGO_BUILD_ARGS) $(CARGO_WORKSPACE_ARGS)
|
|
|
|
|
CARGO_WORKSPACE_ALL_TARGETS_BUILD_ARGS ?= $(CARGO_WORKSPACE_BUILD_ARGS) $(CARGO_ALL_TARGETS_ARGS)
|
|
|
|
|
NEXTEST_STATUS_ARGS ?= --status-level slow --final-status-level slow
|
|
|
|
|
DEV_FEATURES ?= web-ui-dynamic
|
|
|
|
|
DEV_CARGO_FEATURE_ARGS := $(if $(strip $(DEV_FEATURES)),--features "$(DEV_FEATURES)",)
|
|
|
|
|
RELEASE_FEATURES ?=
|
|
|
|
|
RELEASE_CARGO_FEATURE_ARGS := $(if $(strip $(RELEASE_FEATURES)),--features "$(RELEASE_FEATURES)",)
|
|
|
|
|
FEATURE_CHECK_ARGS ?=
|
|
|
|
|
CROSS_PACKAGE ?= synctv
|
|
|
|
|
CROSS_FEATURE_ARGS ?=
|
|
|
|
|
CROSS_LINUX_TARGET ?= x86_64-unknown-linux-gnu
|
|
|
|
|
CROSS_WINDOWS_TARGET ?= x86_64-pc-windows-gnu
|
|
|
|
|
CROSS_DARWIN_TARGET ?= aarch64-apple-darwin
|
|
|
|
|
CROSS_CHECK_ARGS ?= $(CARGO_BUILD_ARGS) -p $(CROSS_PACKAGE) $(CARGO_ALL_TARGETS_ARGS) $(CROSS_FEATURE_ARGS)
|
|
|
|
|
TLS_RING_KEY_CRATES := synctv-core synctv-api synctv-cluster synctv-realtime synctv-livestream synctv-media-providers synctv-proxy
|
|
|
|
|
TLS_RING_KEY_PACKAGE_ARGS := $(foreach crate,$(TLS_RING_KEY_CRATES),-p $(crate))
|
|
|
|
|
TLS_RING_KEY_FEATURE_ARGS := $(foreach crate,$(TLS_RING_KEY_CRATES),$(crate)/tls-ring $(crate)/tls-webpki-roots)
|
|
|
|
|
DEV_SSRF_ENABLED ?= false
|
|
|
|
|
DEV_SSRF_ALLOW_PRIVATE_NETWORK_TARGETS ?= false
|
|
|
|
|
|
|
|
|
|
DEV_DATA_DIR := $(CURDIR)/.dev-data
|
|
|
|
|
DEV_SOCKET := $(DEV_DATA_DIR)/run/synctv.sock
|
|
|
|
|
DEV_BIN ?=
|
|
|
|
|
DEV_DATABASE_URL := postgresql://synctv:synctv@127.0.0.1:5432/synctv
|
|
|
|
|
DEV_REDIS_URL := redis://127.0.0.1:6379
|
|
|
|
|
DEV_ROOT_USERNAME := root
|
|
|
|
|
DEV_ROOT_PASSWORD := LocalDevRootPass2026!
|
|
|
|
|
DEV_JWT_SECRET := local-compose-jwt-secret-not-for-production-2026
|
|
|
|
|
DEV_CLUSTER_SECRET := local-compose-cluster-secret-2026
|
|
|
|
|
DEV_CREDENTIAL_KEY := 222102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
|
|
|
|
|
DEV_EMAIL_OUTBOX_KEY := 333102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
|
|
|
|
|
DEV_TOTP_KEY := 444102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
|
|
|
|
|
DEV_OPAQUE_SECRET := local-compose-opaque-server-setup-secret-not-for-production-2026
|
|
|
|
|
DEV_PROXY_SIGNING_KEY := local-compose-proxy-signing-key-not-for-production-2026
|
|
|
|
|
DEV_MEDIA_SWARM_SIGNING_KEY := local-compose-media-swarm-signing-key-not-for-production-2026
|
|
|
|
|
DEV_PROVIDER_SESSION_KEY := local-compose-provider-session-key-not-for-production-2026
|
|
|
|
|
DEV_LOGIN_DISCOVERY_KEY := local-compose-login-discovery-key-not-for-production-2026
|
|
|
|
|
DEV_WEBAUTHN_ENUMERATION_KEY := local-compose-webauthn-enumeration-key-not-for-production-2026
|
|
|
|
|
DEV_FILE_UPLOAD_TOKEN_SECRET := local-compose-file-upload-token-key-not-for-production-2026
|
|
|
|
|
DEV_AUTH_MAX_REQUESTS ?= 10000
|
|
|
|
|
DEV_AUTH_WINDOW_SECONDS ?= 1
|
|
|
|
|
DEV_CORS_ORIGINS := ["http://localhost:3000","http://127.0.0.1:3000","http://localhost:5173","http://127.0.0.1:5173","http://localhost:8080","http://127.0.0.1:8080"]
|
|
|
|
|
DEV_FILE_STORAGE_BACKENDS := {"database":{"type":"database"}}
|
|
|
|
|
DEV_WEB_UI_DIR ?= $(CURDIR)/synctv-web-ui/dist
|
|
|
|
|
|
|
|
|
|
COMPOSE_DEV := $(COMPOSE) -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE)
|
|
|
|
|
COMPOSE_DEV_PROFILES := COMPOSE_PROFILES=media,storage,auth $(COMPOSE_DEV)
|
|
|
|
|
|
|
|
|
|
define DEV_ENV_EXPORTS
|
|
|
|
|
set -a; \
|
|
|
|
|
if [ -f .env.synctv ]; then source .env.synctv; fi; \
|
|
|
|
|
set +a; \
|
|
|
|
|
export SYNCTV_DATA_DIR="$(DEV_DATA_DIR)"; \
|
|
|
|
|
export SYNCTV_DATABASE_URL="$(DEV_DATABASE_URL)"; \
|
|
|
|
|
export SYNCTV_REDIS_URL="$(DEV_REDIS_URL)"; \
|
feat(logging): add structured transport access logs (#436)
## Summary
- add structured HTTP and gRPC access logs with request correlation,
status-aware levels, response bytes, and complete body lifecycle timing
- route diagnostic and access events through independently configurable
text or JSON outputs with non-blocking writers and dropped-line
accounting
- honor the configured IANA timezone across text, JSON, diagnostic, and
access logs with DST-aware RFC 3339 offsets
- keep HTTP metric labels bounded while logging concrete unmatched paths
without query strings
- reduce local development noise by defaulting global logs to info while
retaining component debug logs
## Behavior
- HTTP 2xx and 3xx complete at info, 4xx at warn, and 5xx or body
failures at error
- slow handlers are raised to warn; ordinary client response
cancellation remains debug
- gRPC completion waits for final trailers and records the canonical
gRPC status
- request IDs are validated, propagated in responses, and included in
mapped gRPC errors
- header latency and full response lifecycle latency are reported
separately
- log timestamps use the configured time.timezone and include a numeric
UTC offset
## Testing
- cargo test -p synctv-api-common transport_access_log --lib
- cargo test -p synctv-core logging::tests --lib
- cargo test -p synctv resource_options::tests --lib
- cargo clippy for affected crates and all targets with warnings denied
- cargo check --workspace --all-targets
- cargo fmt --all -- --check
- git diff --check
- local server smoke tests for request completion, request ID
propagation, byte counts, query omission, and Asia/Shanghai timestamps
in diagnostic and access logs
1 month ago
|
|
|
export SYNCTV_LOGGING_LEVEL="$${SYNCTV_LOGGING_LEVEL:-info}"; \
|
|
|
|
|
export SYNCTV_SERVER_LOGGING_LEVEL="$${SYNCTV_SERVER_LOGGING_LEVEL:-debug}"; \
|
|
|
|
|
export SYNCTV_LIVESTREAM_LOGGING_LEVEL="$${SYNCTV_LIVESTREAM_LOGGING_LEVEL:-debug}"; \
|
|
|
|
|
export SYNCTV_WEBRTC_LOGGING_LEVEL="$${SYNCTV_WEBRTC_LOGGING_LEVEL:-debug}"; \
|
|
|
|
|
export SYNCTV_SERVER_HOST=0.0.0.0; \
|
|
|
|
|
export SYNCTV_SERVER_PORT=8080; \
|
|
|
|
|
export SYNCTV_SERVER_WEB_UI_DIRECTORY="$(DEV_WEB_UI_DIR)"; \
|
|
|
|
|
export SYNCTV_SERVER_CORS_ALLOWED_ORIGINS='$(DEV_CORS_ORIGINS)'; \
|
|
|
|
|
export SYNCTV_JWT_SECRET="$(DEV_JWT_SECRET)"; \
|
|
|
|
|
export SYNCTV_CLUSTER_SECRET="$(DEV_CLUSTER_SECRET)"; \
|
|
|
|
|
export SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY="$(DEV_CREDENTIAL_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_TOTP_ENCRYPTION_KEY="$(DEV_TOTP_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_EMAIL_OUTBOX_ENCRYPTION_KEY="$(DEV_EMAIL_OUTBOX_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET="$(DEV_OPAQUE_SECRET)"; \
|
|
|
|
|
export SYNCTV_SECURITY_PROXY_SIGNING_KEY="$(DEV_PROXY_SIGNING_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_MEDIA_SWARM_SIGNING_KEY="$(DEV_MEDIA_SWARM_SIGNING_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_PROVIDER_SESSION_ENCRYPTION_KEY="$(DEV_PROVIDER_SESSION_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_LOGIN_DISCOVERY_KEY="$(DEV_LOGIN_DISCOVERY_KEY)"; \
|
|
|
|
|
export SYNCTV_SECURITY_WEBAUTHN_ENUMERATION_KEY="$(DEV_WEBAUTHN_ENUMERATION_KEY)"; \
|
|
|
|
|
export SYNCTV_FILE_UPLOAD_TOKEN_SECRET="$(DEV_FILE_UPLOAD_TOKEN_SECRET)"; \
|
|
|
|
|
export SYNCTV_REQUEST_RATE_LIMITS_AUTH_MAX_REQUESTS="$(DEV_AUTH_MAX_REQUESTS)"; \
|
|
|
|
|
export SYNCTV_REQUEST_RATE_LIMITS_AUTH_WINDOW_SECONDS="$(DEV_AUTH_WINDOW_SECONDS)"; \
|
|
|
|
|
export SYNCTV_SECURITY_SSRF_ENABLED="$(DEV_SSRF_ENABLED)"; \
|
|
|
|
|
export SYNCTV_SECURITY_SSRF_ALLOW_PRIVATE_NETWORK_TARGETS="$(DEV_SSRF_ALLOW_PRIVATE_NETWORK_TARGETS)"; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_DEFAULT_BACKEND=database; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_CHAT_ATTACHMENTS_BACKEND=database; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_USER_AVATARS_BACKEND=database; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_MEDIA_COVERS_BACKEND=database; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_ROOM_COVERS_BACKEND=database; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_PLAYLIST_COVERS_BACKEND=database; \
|
|
|
|
|
export SYNCTV_FILE_STORAGE_BACKENDS='$(DEV_FILE_STORAGE_BACKENDS)'; \
|
|
|
|
|
export SYNCTV_BOOTSTRAP_CREATE_ROOT_USER=true; \
|
|
|
|
|
export SYNCTV_BOOTSTRAP_ROOT_USERNAME="$(DEV_ROOT_USERNAME)"; \
|
|
|
|
|
export SYNCTV_BOOTSTRAP_ROOT_PASSWORD="$(DEV_ROOT_PASSWORD)"; \
|
|
|
|
|
export SYNCTV_WEBRTC_ENABLE_BUILTIN_STUN=false; \
|
|
|
|
|
export SYNCTV_MANAGEMENT_ENABLED=true; \
|
|
|
|
|
export SYNCTV_MANAGEMENT_TRANSPORT=unix; \
|
|
|
|
|
export SYNCTV_MANAGEMENT_UNIX_SOCKET_PATH="$(DEV_SOCKET)"
|
|
|
|
|
endef
|
|
|
|
|
|
feat(web): add client-aware playback and reproducible UI (#433)
## Summary
- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous
## Reproducible Web UI
- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes
## Verification
- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows
Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52
Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
|
|
|
.PHONY: help clean compose-init compose-config compose-pull compose-up compose-down compose-logs compose-ps dev-check dev-env dev-up dev-stack dev-build release-build web-ui-build web-release-build dev-serve dev-down dev-clean dev-reset dev-data-reset dev-logs dev-ps dev-status dev-wait dev-shell dev-migrate dev-dropdb dev-db dev-redis dev-open dev-smoke fmt fmt-check check check-all-targets build-workspace proto-freshness feature-check feature-check-key-crates-tls-ring-webpki sqlx-prepare nextest nextest-default nextest-ignored doc-test clippy clippy-check install-cargo-audit audit audit-advisories install-cargo-deny deny-check deny-advisories deny-licenses deny-bans deny-sources install-cargo-udeps udeps cargo-workspace-version set-release-version validate-helm require-cross install-cross cross-linux-check cross-windows-check cross-darwin-check cross-linux-clippy cross-windows-clippy cross-darwin-clippy
|
|
|
|
|
|
|
|
|
|
help: ## Show available targets.
|
|
|
|
|
@awk 'BEGIN {FS = ":.*##"; printf "SyncTV targets:\n"} /^[a-zA-Z0-9_.-]+:.*##/ {printf " %-18s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
|
|
|
|
|
|
|
|
|
compose-init: ## Create production Compose env files with generated secrets.
|
|
|
|
|
@command -v openssl >/dev/null || { printf "openssl is required.\n" >&2; exit 1; }
|
|
|
|
|
@for file in $(COMPOSE_ENV_FILES); do \
|
|
|
|
|
if [ -e "$$file" ]; then \
|
|
|
|
|
printf "%s already exists; refusing to overwrite it.\n" "$$file" >&2; \
|
|
|
|
|
exit 1; \
|
|
|
|
|
fi; \
|
|
|
|
|
done
|
|
|
|
|
@trap 'rm -f $(COMPOSE_ENV_FILES) .env.postgres.bak .env.redis.bak .env.synctv.bak' ERR; \
|
|
|
|
|
umask 077; \
|
|
|
|
|
cp .env.postgres.example .env.postgres; \
|
|
|
|
|
cp .env.redis.example .env.redis; \
|
|
|
|
|
cp .env.synctv.example .env.synctv; \
|
|
|
|
|
set_env() { \
|
|
|
|
|
local file="$$1" key="$$2" value="$$3"; \
|
|
|
|
|
sed -i.bak "s|^$${key}=.*|$${key}=$${value}|" "$$file"; \
|
|
|
|
|
}; \
|
|
|
|
|
postgres_password="$$(openssl rand -hex 32)"; \
|
|
|
|
|
redis_password="$$(openssl rand -hex 32)"; \
|
|
|
|
|
set_env .env.postgres POSTGRES_PASSWORD "$$postgres_password"; \
|
|
|
|
|
set_env .env.redis REDIS_PASSWORD "$$redis_password"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_DATABASE_URL "postgresql://synctv:$${postgres_password}@postgres:5432/synctv"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_REDIS_URL "redis://:$${redis_password}@redis:6379"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_JWT_SECRET "$$(openssl rand -base64 32)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_CLUSTER_SECRET "$$(openssl rand -hex 32)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY "$$(openssl rand -hex 32)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_TOTP_ENCRYPTION_KEY "$$(openssl rand -hex 32)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_EMAIL_OUTBOX_ENCRYPTION_KEY "$$(openssl rand -hex 32)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET "$$(openssl rand -base64 48)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_PROXY_SIGNING_KEY "$$(openssl rand -base64 48)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_MEDIA_SWARM_SIGNING_KEY "$$(openssl rand -base64 48)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_PROVIDER_SESSION_ENCRYPTION_KEY "$$(openssl rand -base64 48)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_LOGIN_DISCOVERY_KEY "$$(openssl rand -base64 48)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_SECURITY_WEBAUTHN_ENUMERATION_KEY "$$(openssl rand -base64 48)"; \
|
|
|
|
|
set_env .env.synctv SYNCTV_FILE_UPLOAD_TOKEN_SECRET "$$(openssl rand -base64 48)"; \
|
|
|
|
|
rm -f .env.postgres.bak .env.redis.bak .env.synctv.bak; \
|
|
|
|
|
chmod 600 $(COMPOSE_ENV_FILES); \
|
|
|
|
|
trap - ERR
|
|
|
|
|
@printf "Created production Compose env files. Set SYNCTV_BOOTSTRAP_ROOT_PASSWORD in .env.synctv, then run 'make compose-up'.\n"
|
|
|
|
|
|
|
|
|
|
compose-config: ## Render and validate the production Compose configuration.
|
|
|
|
|
$(COMPOSE_PROD) config
|
|
|
|
|
|
|
|
|
|
compose-pull: ## Pull production Compose images.
|
|
|
|
|
$(COMPOSE_PROD) pull
|
|
|
|
|
|
|
|
|
|
compose-up: ## Validate and start the production Compose stack.
|
|
|
|
|
$(COMPOSE_PROD) config --quiet
|
|
|
|
|
$(COMPOSE_PROD) up -d
|
|
|
|
|
|
|
|
|
|
compose-down: ## Stop the production Compose stack and keep volumes.
|
|
|
|
|
$(COMPOSE_PROD) down --remove-orphans
|
|
|
|
|
|
|
|
|
|
compose-logs: ## Follow production Compose logs. Set SERVICE=name to focus.
|
|
|
|
|
$(COMPOSE_PROD) logs -f --tail=100 $(SERVICE)
|
|
|
|
|
|
|
|
|
|
compose-ps: ## Show production Compose service status.
|
|
|
|
|
$(COMPOSE_PROD) ps
|
|
|
|
|
|
|
|
|
|
dev-check: ## Check required local tools.
|
|
|
|
|
@command -v docker >/dev/null
|
|
|
|
|
@$(COMPOSE) version >/dev/null
|
|
|
|
|
@command -v rustup >/dev/null
|
|
|
|
|
@$(CARGO) --version >/dev/null
|
feat(web): add client-aware playback and reproducible UI (#433)
## Summary
- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous
## Reproducible Web UI
- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes
## Verification
- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows
Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52
Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
|
|
|
@printf "Docker, Docker Compose, and Cargo are available.\n"
|
|
|
|
|
|
|
|
|
|
dev-env: ## Print local service URLs and credentials.
|
|
|
|
|
@printf "SyncTV: http://127.0.0.1:8080 root / %s\n" "$(DEV_ROOT_PASSWORD)"
|
|
|
|
|
@printf "Postgres: %s\n" "$(DEV_DATABASE_URL)"
|
|
|
|
|
@printf "Redis: %s\n" "$(DEV_REDIS_URL)"
|
|
|
|
|
@printf "OpenList: http://127.0.0.1:5244 admin / synctv-openlist\n"
|
|
|
|
|
@printf "Nextcloud: http://127.0.0.1:8082 synctv / synctv-nextcloud\n"
|
|
|
|
|
@printf "Seafile: http://127.0.0.1:8083 synctv@example.com / synctv-seafile\n"
|
|
|
|
|
@printf "Emby: http://127.0.0.1:8096 MyEmbyUser / synctv-emby\n"
|
|
|
|
|
@printf "Jellyfin: http://127.0.0.1:8097 root / synctv-jellyfin\n"
|
|
|
|
|
@printf "RustFS: http://127.0.0.1:9000 rustfsadmin / rustfsadmin\n"
|
|
|
|
|
@printf "RustFS UI: http://127.0.0.1:9001\n"
|
|
|
|
|
@printf "Casdoor: http://127.0.0.1:8000 admin / 123\n"
|
|
|
|
|
|
|
|
|
|
dev-up: dev-check ## Start core development dependencies: PostgreSQL and Redis.
|
|
|
|
|
$(COMPOSE_DEV) up -d $(DEV_BASE_SERVICES)
|
|
|
|
|
@$(MAKE) dev-wait SERVICES="$(DEV_BASE_SERVICES)"
|
|
|
|
|
|
|
|
|
|
dev-stack: dev-up ## Start media, storage, and auth development dependencies after core services.
|
|
|
|
|
@$(COMPOSE_DEV) exec -T postgres psql -U synctv -d synctv -tc "SELECT 1 FROM pg_database WHERE datname = 'casdoor'" | grep -q 1 || \
|
|
|
|
|
$(COMPOSE_DEV) exec -T postgres createdb -U synctv casdoor
|
|
|
|
|
$(COMPOSE_DEV_PROFILES) up -d $(DEV_OPTIONAL_SERVICES) rustfs-init openlist-init nextcloud-init seafile-init emby-init jellyfin-init
|
|
|
|
|
@$(MAKE) dev-wait SERVICES="$(DEV_STACK_WAIT_SERVICES)"
|
|
|
|
|
@$(MAKE) dev-env
|
|
|
|
|
|
|
|
|
|
dev-build: ## Build the local SyncTV binary.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) build $(CARGO_BUILD_ARGS) -p synctv --bin synctv $(DEV_CARGO_FEATURE_ARGS)
|
|
|
|
|
|
|
|
|
|
release-build: ## Build the optimized SyncTV release binary.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) build $(CARGO_BUILD_ARGS) --release -p synctv --bin synctv $(RELEASE_CARGO_FEATURE_ARGS)
|
|
|
|
|
|
feat(web): add client-aware playback and reproducible UI (#433)
## Summary
- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous
## Reproducible Web UI
- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes
## Verification
- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows
Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52
Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
|
|
|
WEB_UI_EXPORT_DIR ?= $(CURDIR)/target/web-ui-dist
|
|
|
|
|
|
|
|
|
|
web-ui-build: ## Build Web assets from synctv-web-ui config into WEB_UI_EXPORT_DIR.
|
|
|
|
|
SYNCTV_WEB_EXPORT_DIR="$(WEB_UI_EXPORT_DIR)" \
|
|
|
|
|
$(CARGO) check $(CARGO_BUILD_ARGS) -p synctv-web-ui --features embed
|
|
|
|
|
|
|
|
|
|
web-release-build: ## Build and embed Web UI using synctv-web-ui config. Set SYNCTV_WEB_CONFIG or use web-ui.local.toml.
|
|
|
|
|
@features="web-ui"; \
|
|
|
|
|
if [ -n "$(RELEASE_FEATURES)" ]; then features="$$features,$(RELEASE_FEATURES)"; fi; \
|
|
|
|
|
SQLX_OFFLINE=true \
|
|
|
|
|
$(CARGO) build $(CARGO_BUILD_ARGS) --release -p synctv --bin synctv --features "$$features"
|
|
|
|
|
|
|
|
|
|
dev-serve: dev-up ## Run SyncTV locally with development defaults.
|
|
|
|
|
mkdir -p "$(DEV_DATA_DIR)/run"
|
|
|
|
|
@if [ -n "$(DEV_BIN)" ]; then \
|
|
|
|
|
$(DEV_ENV_EXPORTS); \
|
|
|
|
|
exec "$(DEV_BIN)" serve; \
|
|
|
|
|
else \
|
|
|
|
|
$(DEV_ENV_EXPORTS); \
|
|
|
|
|
SQLX_OFFLINE=true exec $(CARGO) run $(CARGO_BUILD_ARGS) -p synctv --bin synctv $(DEV_CARGO_FEATURE_ARGS) -- serve; \
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
dev-down: ## Stop development containers and keep volumes.
|
|
|
|
|
$(COMPOSE_DEV_PROFILES) down --remove-orphans
|
|
|
|
|
|
|
|
|
|
dev-clean: ## Stop development containers and remove Compose volumes.
|
|
|
|
|
$(COMPOSE_DEV_PROFILES) down -v --remove-orphans
|
|
|
|
|
|
|
|
|
|
dev-data-reset: ## Remove local .dev-data used by dev-serve.
|
|
|
|
|
rm -rf "$(DEV_DATA_DIR)"
|
|
|
|
|
mkdir -p "$(DEV_DATA_DIR)/run"
|
|
|
|
|
|
|
|
|
|
dev-reset: dev-clean dev-data-reset ## Remove containers, volumes, and local dev data.
|
|
|
|
|
|
|
|
|
|
dev-logs: ## Tail development dependency logs. Set SERVICE=name to focus.
|
|
|
|
|
$(COMPOSE_DEV_PROFILES) logs -f --tail=$(DEV_LOG_TAIL) $(SERVICE)
|
|
|
|
|
|
|
|
|
|
dev-ps: ## Show development container status.
|
|
|
|
|
$(COMPOSE_DEV_PROFILES) ps
|
|
|
|
|
|
|
|
|
|
dev-status: dev-ps ## Show container status and local environment.
|
|
|
|
|
@$(MAKE) dev-env
|
|
|
|
|
|
|
|
|
|
dev-wait: ## Wait for Compose services to become healthy/running. Set SERVICES="postgres redis".
|
|
|
|
|
@services="$${SERVICES:-$(DEV_BASE_SERVICES)}"; \
|
|
|
|
|
deadline=$$((SECONDS + $(DEV_WAIT_TIMEOUT))); \
|
|
|
|
|
for service in $$services; do \
|
|
|
|
|
printf "Waiting for %s" "$$service"; \
|
|
|
|
|
while true; do \
|
|
|
|
|
id="$$( $(COMPOSE_DEV_PROFILES) ps -a -q "$$service" 2>/dev/null || true )"; \
|
|
|
|
|
if [ -n "$$id" ]; then \
|
|
|
|
|
health="$$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$$id" 2>/dev/null || true)"; \
|
|
|
|
|
exit_code="$$(docker inspect -f '{{.State.ExitCode}}' "$$id" 2>/dev/null || echo 1)"; \
|
|
|
|
|
case "$$health" in \
|
|
|
|
|
healthy|running) printf " %s\n" "$$health"; break ;; \
|
|
|
|
|
exited) \
|
|
|
|
|
if [ "$$exit_code" = "0" ]; then \
|
|
|
|
|
printf " completed\n"; \
|
|
|
|
|
break; \
|
|
|
|
|
fi; \
|
|
|
|
|
printf "\n%s exited before becoming ready.\n" "$$service"; \
|
|
|
|
|
exit 1; \
|
|
|
|
|
;; \
|
|
|
|
|
dead) printf "\n%s exited before becoming ready.\n" "$$service"; exit 1 ;; \
|
|
|
|
|
esac; \
|
|
|
|
|
fi; \
|
|
|
|
|
if [ "$$SECONDS" -ge "$$deadline" ]; then \
|
|
|
|
|
printf "\nTimed out waiting for %s.\n" "$$service"; \
|
|
|
|
|
$(COMPOSE_DEV_PROFILES) ps "$$service"; \
|
|
|
|
|
exit 1; \
|
|
|
|
|
fi; \
|
|
|
|
|
printf "."; \
|
|
|
|
|
sleep 2; \
|
|
|
|
|
done; \
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
dev-shell: dev-up ## Open a shell with SyncTV development environment variables.
|
|
|
|
|
mkdir -p "$(DEV_DATA_DIR)/run"
|
|
|
|
|
env \
|
|
|
|
|
SYNCTV_DATA_DIR="$(DEV_DATA_DIR)" \
|
|
|
|
|
SYNCTV_DATABASE_URL="$(DEV_DATABASE_URL)" \
|
|
|
|
|
SYNCTV_REDIS_URL="$(DEV_REDIS_URL)" \
|
|
|
|
|
SYNCTV_SERVER_WEB_UI_DIRECTORY="$(DEV_WEB_UI_DIR)" \
|
|
|
|
|
SYNCTV_JWT_SECRET="$(DEV_JWT_SECRET)" \
|
|
|
|
|
SYNCTV_CLUSTER_SECRET="$(DEV_CLUSTER_SECRET)" \
|
|
|
|
|
SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY="$(DEV_CREDENTIAL_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_TOTP_ENCRYPTION_KEY="$(DEV_TOTP_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_EMAIL_OUTBOX_ENCRYPTION_KEY="$(DEV_EMAIL_OUTBOX_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET="$(DEV_OPAQUE_SECRET)" \
|
|
|
|
|
SYNCTV_SECURITY_PROXY_SIGNING_KEY="$(DEV_PROXY_SIGNING_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_MEDIA_SWARM_SIGNING_KEY="$(DEV_MEDIA_SWARM_SIGNING_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_PROVIDER_SESSION_ENCRYPTION_KEY="$(DEV_PROVIDER_SESSION_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_LOGIN_DISCOVERY_KEY="$(DEV_LOGIN_DISCOVERY_KEY)" \
|
|
|
|
|
SYNCTV_SECURITY_WEBAUTHN_ENUMERATION_KEY="$(DEV_WEBAUTHN_ENUMERATION_KEY)" \
|
|
|
|
|
SYNCTV_FILE_UPLOAD_TOKEN_SECRET="$(DEV_FILE_UPLOAD_TOKEN_SECRET)" \
|
|
|
|
|
SYNCTV_BOOTSTRAP_CREATE_ROOT_USER=true \
|
|
|
|
|
SYNCTV_BOOTSTRAP_ROOT_USERNAME="$(DEV_ROOT_USERNAME)" \
|
|
|
|
|
SYNCTV_BOOTSTRAP_ROOT_PASSWORD="$(DEV_ROOT_PASSWORD)" \
|
|
|
|
|
$${SHELL:-/bin/bash}
|
|
|
|
|
|
|
|
|
|
dev-migrate: dev-up ## Run database migrations against the local PostgreSQL container.
|
|
|
|
|
DATABASE_URL="$(DEV_DATABASE_URL)" $(CARGO) sqlx migrate run
|
|
|
|
|
|
|
|
|
|
dev-dropdb: dev-up ## Drop and recreate the local development PostgreSQL database.
|
|
|
|
|
$(COMPOSE_DEV) exec -T postgres sh -lc 'dropdb -U synctv --if-exists synctv && createdb -U synctv synctv'
|
|
|
|
|
|
|
|
|
|
clean: ## Remove Rust workspace build artifacts.
|
|
|
|
|
$(CARGO) clean
|
|
|
|
|
|
|
|
|
|
sqlx-prepare: dev-migrate ## Refresh SQLx offline metadata in .sqlx.
|
|
|
|
|
DATABASE_URL="$(DEV_DATABASE_URL)" $(CARGO) sqlx prepare --workspace -- --all-targets
|
|
|
|
|
|
|
|
|
|
fmt: ## Format all Rust code.
|
|
|
|
|
$(CARGO) fmt --all
|
|
|
|
|
|
|
|
|
|
fmt-check: ## Check Rust formatting without modifying files.
|
|
|
|
|
$(CARGO) fmt --all -- --check
|
|
|
|
|
|
|
|
|
|
check: ## Check workspace library and binary targets.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) check $(CARGO_WORKSPACE_BUILD_ARGS)
|
|
|
|
|
|
|
|
|
|
check-all-targets: ## Check all workspace targets, including tests, benches, and examples.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) check $(CARGO_WORKSPACE_ALL_TARGETS_BUILD_ARGS)
|
|
|
|
|
|
|
|
|
|
build-workspace: ## Build the locked workspace dependency graph.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) build $(CARGO_WORKSPACE_BUILD_ARGS) --verbose
|
|
|
|
|
|
|
|
|
|
proto-freshness: ## Regenerate protobuf artifacts and require a clean generated diff.
|
|
|
|
|
SYNCTV_REGEN_PROTO=1 SQLX_OFFLINE=true $(CARGO) check $(CARGO_BUILD_ARGS) -p synctv-proto -p synctv-media-providers -p synctv-cluster -p synctv-livestream -p synctv-realtime -p synctv-proxy
|
|
|
|
|
git diff --exit-code -- synctv-proto/src synctv-media-providers/src/proto
|
|
|
|
|
|
|
|
|
|
feature-check: ## Check synctv with FEATURE_CHECK_ARGS supplied by CI or the caller.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) check $(CARGO_BUILD_ARGS) -p synctv $(FEATURE_CHECK_ARGS)
|
|
|
|
|
|
|
|
|
|
feature-check-key-crates-tls-ring-webpki: ## Check key crates with the ring/webpki TLS feature pair.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) check $(CARGO_BUILD_ARGS) $(TLS_RING_KEY_PACKAGE_ARGS) --no-default-features --features "$(TLS_RING_KEY_FEATURE_ARGS)"
|
|
|
|
|
|
|
|
|
|
nextest: ## Run the full workspace nextest suite, including ignored tests.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) nextest run $(CARGO_WORKSPACE_BUILD_ARGS) --run-ignored all --nff $(NEXTEST_STATUS_ARGS)
|
|
|
|
|
|
|
|
|
|
nextest-default: ## Run non-ignored workspace tests with nextest.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) nextest run $(CARGO_WORKSPACE_BUILD_ARGS) --run-ignored default --nff $(NEXTEST_STATUS_ARGS)
|
|
|
|
|
|
|
|
|
|
nextest-ignored: ## Run ignored workspace tests with nextest.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) nextest run $(CARGO_WORKSPACE_BUILD_ARGS) --run-ignored only --nff $(NEXTEST_STATUS_ARGS)
|
|
|
|
|
|
|
|
|
|
doc-test: ## Run locked workspace documentation tests.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) test $(CARGO_WORKSPACE_BUILD_ARGS) --doc
|
|
|
|
|
|
|
|
|
|
clippy: ## Apply Clippy fixes, then require a clean workspace lint pass.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) clippy $(CARGO_WORKSPACE_ALL_TARGETS_BUILD_ARGS) --fix --allow-dirty
|
|
|
|
|
|
|
|
|
|
clippy-check: ## Run locked workspace Clippy checks without modifying files.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) clippy $(CARGO_WORKSPACE_ALL_TARGETS_BUILD_ARGS)
|
|
|
|
|
|
|
|
|
|
install-cargo-audit: ## Install cargo-audit for CI security checks.
|
|
|
|
|
$(CARGO) install cargo-audit $(CARGO_LOCKED)
|
|
|
|
|
|
|
|
|
|
audit: ## Fail on RustSec vulnerabilities.
|
|
|
|
|
$(CARGO) audit
|
|
|
|
|
|
|
|
|
|
audit-advisories: ## Print the complete RustSec advisory report.
|
|
|
|
|
$(CARGO) audit
|
|
|
|
|
|
|
|
|
|
install-cargo-deny: ## Install cargo-deny for dependency policy checks.
|
|
|
|
|
$(CARGO) install cargo-deny $(CARGO_LOCKED)
|
|
|
|
|
|
|
|
|
|
deny-check: ## Run all cargo-deny policy checks.
|
|
|
|
|
$(CARGO) deny check
|
|
|
|
|
|
|
|
|
|
deny-advisories:
|
|
|
|
|
$(CARGO) deny check advisories
|
|
|
|
|
|
|
|
|
|
deny-licenses:
|
|
|
|
|
$(CARGO) deny check licenses
|
|
|
|
|
|
|
|
|
|
deny-bans:
|
|
|
|
|
$(CARGO) deny check bans
|
|
|
|
|
|
|
|
|
|
deny-sources:
|
|
|
|
|
$(CARGO) deny check sources
|
|
|
|
|
|
|
|
|
|
install-cargo-udeps: ## Install cargo-udeps for unused dependency checks.
|
|
|
|
|
$(CARGO) install cargo-udeps $(CARGO_LOCKED)
|
|
|
|
|
|
|
|
|
|
udeps: ## Check all workspace targets for unused dependencies.
|
|
|
|
|
SQLX_OFFLINE=true $(CARGO) udeps $(CARGO_WORKSPACE_ALL_TARGETS_BUILD_ARGS)
|
|
|
|
|
|
|
|
|
|
cargo-workspace-version: ## Print the Cargo workspace version.
|
|
|
|
|
@$(CARGO) metadata --format-version 1 --no-deps $(CARGO_LOCKED) | node -e 'const fs = require("fs"); const meta = JSON.parse(fs.readFileSync(0, "utf8")); const id = meta.workspace_default_members[0]; process.stdout.write((meta.packages.find((pkg) => pkg.id === id) || meta.packages[0]).version);'
|
|
|
|
|
|
|
|
|
|
set-release-version: ## Synchronize release files. Set VERSION=x.y.z.
|
|
|
|
|
@test -n "$(VERSION)" || { printf "VERSION is required.\n" >&2; exit 1; }
|
feat(web): add client-aware playback and reproducible UI (#433)
## Summary
- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous
## Reproducible Web UI
- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes
## Verification
- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows
Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52
Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
|
|
|
scripts/set-release-version.sh "$(VERSION)"
|
|
|
|
|
|
|
|
|
|
validate-helm: ## Validate Helm charts and rendered SyncTV configuration.
|
feat(web): add client-aware playback and reproducible UI (#433)
## Summary
- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous
## Reproducible Web UI
- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes
## Verification
- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows
Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52
Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
|
|
|
scripts/validate-helm.sh
|
|
|
|
|
|
|
|
|
|
require-cross:
|
|
|
|
|
@command -v cargo-cross >/dev/null || { printf "cargo-cross is required; run 'make install-cross'.\n" >&2; exit 1; }
|
|
|
|
|
|
|
|
|
|
install-cross: ## Install cargo-cross using the configured Rust toolchain.
|
|
|
|
|
$(CARGO) install cargo-cross $(CARGO_LOCKED)
|
|
|
|
|
|
|
|
|
|
cross-linux-check: require-cross ## Cross-check SyncTV for Linux.
|
|
|
|
|
SQLX_OFFLINE=true $(CROSS) check $(CROSS_CHECK_ARGS) --target "$(CROSS_LINUX_TARGET)"
|
|
|
|
|
|
|
|
|
|
cross-windows-check: require-cross ## Cross-check SyncTV for Windows GNU.
|
|
|
|
|
SQLX_OFFLINE=true $(CROSS) check $(CROSS_CHECK_ARGS) --target "$(CROSS_WINDOWS_TARGET)"
|
|
|
|
|
|
|
|
|
|
cross-darwin-check: require-cross ## Cross-check SyncTV for Darwin.
|
|
|
|
|
SQLX_OFFLINE=true $(CROSS) check $(CROSS_CHECK_ARGS) --target "$(CROSS_DARWIN_TARGET)"
|
|
|
|
|
|
|
|
|
|
cross-linux-clippy: require-cross ## Run cross Clippy for Linux.
|
|
|
|
|
SQLX_OFFLINE=true $(CROSS) clippy $(CROSS_CHECK_ARGS) --target "$(CROSS_LINUX_TARGET)"
|
|
|
|
|
|
|
|
|
|
cross-windows-clippy: require-cross ## Run cross Clippy for Windows GNU.
|
|
|
|
|
SQLX_OFFLINE=true $(CROSS) clippy $(CROSS_CHECK_ARGS) --target "$(CROSS_WINDOWS_TARGET)"
|
|
|
|
|
|
|
|
|
|
cross-darwin-clippy: require-cross ## Run cross Clippy for Darwin.
|
|
|
|
|
SQLX_OFFLINE=true $(CROSS) clippy $(CROSS_CHECK_ARGS) --target "$(CROSS_DARWIN_TARGET)"
|
|
|
|
|
|
|
|
|
|
dev-db: dev-up ## Open psql inside the PostgreSQL container.
|
|
|
|
|
$(COMPOSE_DEV) exec postgres psql -U synctv -d synctv
|
|
|
|
|
|
|
|
|
|
dev-redis: dev-up ## Open redis-cli inside the Redis container.
|
|
|
|
|
$(COMPOSE_DEV) exec redis redis-cli
|
|
|
|
|
|
|
|
|
|
dev-open: ## Open common local service URLs on macOS.
|
|
|
|
|
@open http://127.0.0.1:8080 || true
|
|
|
|
|
@open http://127.0.0.1:5244 || true
|
|
|
|
|
@open http://127.0.0.1:9001 || true
|
|
|
|
|
@open http://127.0.0.1:8000 || true
|
|
|
|
|
|
|
|
|
|
dev-smoke: ## Run real CLI/curl smoke tests against the local dev stack.
|
|
|
|
|
scripts/dev-e2e-smoke.sh
|