From 617236558e602cf793ee43c049c1fbffd838165b Mon Sep 17 00:00:00 2001 From: Andreas Dueren Date: Thu, 20 Nov 2025 11:54:40 -0600 Subject: [PATCH] Bundle families frontend and billing data --- CHANGELOG.md | 5 +- CloudronManifest.json | 2 +- Dockerfile | 44 +++++++- data/billing/in-testing.json | 186 +++++++++++++++++++++++++++++++ data/billing/us-testing.json | 186 +++++++++++++++++++++++++++++++ patches/museum-family-link.patch | 31 ++++++ start.sh | 2 + 7 files changed, 449 insertions(+), 7 deletions(-) create mode 100644 data/billing/in-testing.json create mode 100644 data/billing/us-testing.json create mode 100644 patches/museum-family-link.patch diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aafea7..092cac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -# Changelog +## 0.5.10 (2025-11-20) + +* Bundle the Ente Families web app so `family.` serves the correct invite/management UI instead of the placeholder photos build. +* Ship built-in billing plan JSON so Museum can resolve subscriptions (`family/add-member`, invite acceptance) on self-hosted installs without manual DB edits. ## 0.5.7 (2025-11-18) diff --git a/CloudronManifest.json b/CloudronManifest.json index 45b5ac8..3341200 100644 --- a/CloudronManifest.json +++ b/CloudronManifest.json @@ -7,7 +7,7 @@ "contactEmail": "contact@ente.io", "website": "https://ente.io", "tagline": "Open source, end-to-end encrypted photo backup", - "version": "0.5.7", + "version": "0.5.10", "upstreamVersion": "git-main", "healthCheckPath": "/health", "httpPort": 3080, diff --git a/Dockerfile b/Dockerfile index 827952c..7ea7880 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,28 @@ # syntax=docker/dockerfile:1 ARG ENTE_GIT_REF=main +ARG FAMILIES_GIT_REF=main FROM debian:bookworm AS ente-source ARG ENTE_GIT_REF +COPY patches /patches +RUN set -e; \ + apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates git patch && \ + git clone --depth=1 --branch "${ENTE_GIT_REF}" https://github.com/ente-io/ente.git /src && \ + if [ -d /patches ]; then \ + for patch_file in /patches/*.patch; do \ + [ -f "$patch_file" ] || continue; \ + (cd /src && patch -p1 < "$patch_file"); \ + done; \ + fi && \ + rm -rf /var/lib/apt/lists/* + +FROM debian:bookworm AS families-source +ARG FAMILIES_GIT_REF RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates git && \ - git clone --depth=1 --branch "${ENTE_GIT_REF}" https://github.com/ente-io/ente.git /src && \ + git clone --depth=1 --branch "${FAMILIES_GIT_REF}" https://github.com/ente-io/families.git /families && \ rm -rf /var/lib/apt/lists/* FROM golang:1.24-bookworm AS museum-builder @@ -63,9 +79,26 @@ RUN if [ -d "apps" ]; then \ printf '

Ente %s

Build output missing.

\n' "${app}" > "/build/web/${app}/index.html"; \ done; \ fi && \ - rm -rf /build/web/albums /build/web/family && \ - cp -r /build/web/photos /build/web/albums && \ - cp -r /build/web/photos /build/web/family + rm -rf /build/web/albums && \ + cp -r /build/web/photos /build/web/albums + +FROM node:20-bookworm-slim AS families-builder +ENV NEXT_PUBLIC_ENTE_ENDPOINT=ENTE_API_ORIGIN_PLACEHOLDER \ + NEXT_WEB_ENTE_ENDPOINT=ENTE_WEB_ENDPOINT_PLACEHOLDER \ + NEXT_PUBLIC_IS_SENTRY_ENABLED=no \ + NEXT_PUBLIC_SENTRY_ENV=local \ + NEXT_PUBLIC_SENTRY_DSN= \ + NEXT_TELEMETRY_DISABLED=1 +COPY --from=families-source /families /families +WORKDIR /families +RUN apt-get update && \ + apt-get install -y --no-install-recommends build-essential python3 && \ + rm -rf /var/lib/apt/lists/* +RUN corepack enable +RUN yarn install --network-timeout 1000000 +RUN mkdir -p /build/family && \ + yarn build && \ + ./node_modules/.bin/next export -o /build/family FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c @@ -80,6 +113,7 @@ RUN apt-get update && \ RUN mkdir -p /app/pkg /app/web "$HOME" && chown -R cloudron:cloudron /app /app/web "$HOME" COPY --from=ente-source /src ${APP_DIR} +COPY data ${APP_DIR}/data RUN rm -rf ${APP_DIR}/.git RUN mkdir -p /app/museum-bin @@ -98,7 +132,7 @@ COPY --from=web-builder /build/web/accounts /app/web/accounts COPY --from=web-builder /build/web/auth /app/web/auth COPY --from=web-builder /build/web/cast /app/web/cast COPY --from=web-builder /build/web/albums /app/web/albums -COPY --from=web-builder /build/web/family /app/web/family +COPY --from=families-builder /build/family /app/web/family COPY start.sh /app/pkg/start.sh COPY admin-helper.sh /app/pkg/admin-helper.sh diff --git a/data/billing/in-testing.json b/data/billing/in-testing.json new file mode 100644 index 0000000..8f4fffa --- /dev/null +++ b/data/billing/in-testing.json @@ -0,0 +1,186 @@ +{ + "IN": [ + { + "id": "50gb_monthly_v4", + "androidID": "50gb_monthly_v4", + "iosID": "50gb_monthly_v4", + "stripeID": "50gb_monthly_v4", + "storage": 53687091200, + "price": "₹0", + "period": "month" + }, + { + "id": "200gb_monthly_v4", + "androidID": "200gb_monthly_v4", + "iosID": "200gb_monthly_v4", + "stripeID": "200gb_monthly_v4", + "storage": 214748364800, + "price": "₹0", + "period": "month" + }, + { + "id": "1000gb_monthly_v4", + "androidID": "1000gb_monthly_v4", + "iosID": "1000gb_monthly_v4", + "stripeID": "1000gb_monthly_v4", + "storage": 1073741824000, + "price": "₹0", + "period": "month" + }, + { + "id": "2000gb_monthly_v4", + "androidID": "2000gb_monthly_v4", + "iosID": "2000gb_monthly_v4", + "stripeID": "2000gb_monthly_v4", + "storage": 2147483648000, + "price": "₹0", + "period": "month" + }, + { + "id": "50gb_yearly_v4", + "androidID": "50gb_yearly_v4", + "iosID": "50gb_yearly_v4", + "stripeID": "50gb_yearly_v4", + "storage": 53687091200, + "price": "₹0", + "period": "year" + }, + { + "id": "200gb_yearly_v4", + "androidID": "200gb_yearly_v4", + "iosID": "200gb_yearly_v4", + "stripeID": "200gb_yearly_v4", + "storage": 214748364800, + "price": "₹0", + "period": "year" + }, + { + "id": "1000gb_yearly_v4", + "androidID": "1000gb_yearly_v4", + "iosID": "1000gb_yearly_v4", + "stripeID": "1000gb_yearly_v4", + "storage": 1073741824000, + "price": "₹0", + "period": "year" + }, + { + "id": "2000gb_yearly_v4", + "androidID": "2000gb_yearly_v4", + "iosID": "2000gb_yearly_v4", + "stripeID": "2000gb_yearly_v4", + "storage": 2147483648000, + "price": "₹0", + "period": "year" + }, + { + "id": "family", + "androidID": "family", + "iosID": "family", + "stripeID": "family", + "storage": 2147483648000, + "price": "₹0", + "period": "year" + }, + { + "id": "free", + "androidID": "free", + "iosID": "free", + "stripeID": "free", + "storage": 10737418240, + "price": "₹0", + "period": "year" + } + ], + "US": [ + { + "id": "50gb_monthly_v4", + "androidID": "50gb_monthly_v4", + "iosID": "50gb_monthly_v4", + "stripeID": "50gb_monthly_v4", + "storage": 53687091200, + "price": "$0", + "period": "month" + }, + { + "id": "200gb_monthly_v4", + "androidID": "200gb_monthly_v4", + "iosID": "200gb_monthly_v4", + "stripeID": "200gb_monthly_v4", + "storage": 214748364800, + "price": "$0", + "period": "month" + }, + { + "id": "1000gb_monthly_v4", + "androidID": "1000gb_monthly_v4", + "iosID": "1000gb_monthly_v4", + "stripeID": "1000gb_monthly_v4", + "storage": 1073741824000, + "price": "$0", + "period": "month" + }, + { + "id": "2000gb_monthly_v4", + "androidID": "2000gb_monthly_v4", + "iosID": "2000gb_monthly_v4", + "stripeID": "2000gb_monthly_v4", + "storage": 2147483648000, + "price": "$0", + "period": "month" + }, + { + "id": "50gb_yearly_v4", + "androidID": "50gb_yearly_v4", + "iosID": "50gb_yearly_v4", + "stripeID": "50gb_yearly_v4", + "storage": 53687091200, + "price": "$0", + "period": "year" + }, + { + "id": "200gb_yearly_v4", + "androidID": "200gb_yearly_v4", + "iosID": "200gb_yearly_v4", + "stripeID": "200gb_yearly_v4", + "storage": 214748364800, + "price": "$0", + "period": "year" + }, + { + "id": "1000gb_yearly_v4", + "androidID": "1000gb_yearly_v4", + "iosID": "1000gb_yearly_v4", + "stripeID": "1000gb_yearly_v4", + "storage": 1073741824000, + "price": "$0", + "period": "year" + }, + { + "id": "2000gb_yearly_v4", + "androidID": "2000gb_yearly_v4", + "iosID": "2000gb_yearly_v4", + "stripeID": "2000gb_yearly_v4", + "storage": 2147483648000, + "price": "$0", + "period": "year" + }, + { + "id": "family", + "androidID": "family", + "iosID": "family", + "stripeID": "family", + "storage": 2147483648000, + "price": "$0", + "period": "year" + }, + { + "id": "free", + "androidID": "free", + "iosID": "free", + "stripeID": "free", + "storage": 10737418240, + "price": "$0", + "period": "year" + } + ] +} diff --git a/data/billing/us-testing.json b/data/billing/us-testing.json new file mode 100644 index 0000000..0acd32e --- /dev/null +++ b/data/billing/us-testing.json @@ -0,0 +1,186 @@ +{ + "US": [ + { + "id": "50gb_monthly_v4", + "androidID": "50gb_monthly_v4", + "iosID": "50gb_monthly_v4", + "stripeID": "50gb_monthly_v4", + "storage": 53687091200, + "price": "$0", + "period": "month" + }, + { + "id": "200gb_monthly_v4", + "androidID": "200gb_monthly_v4", + "iosID": "200gb_monthly_v4", + "stripeID": "200gb_monthly_v4", + "storage": 214748364800, + "price": "$0", + "period": "month" + }, + { + "id": "1000gb_monthly_v4", + "androidID": "1000gb_monthly_v4", + "iosID": "1000gb_monthly_v4", + "stripeID": "1000gb_monthly_v4", + "storage": 1073741824000, + "price": "$0", + "period": "month" + }, + { + "id": "2000gb_monthly_v4", + "androidID": "2000gb_monthly_v4", + "iosID": "2000gb_monthly_v4", + "stripeID": "2000gb_monthly_v4", + "storage": 2147483648000, + "price": "$0", + "period": "month" + }, + { + "id": "50gb_yearly_v4", + "androidID": "50gb_yearly_v4", + "iosID": "50gb_yearly_v4", + "stripeID": "50gb_yearly_v4", + "storage": 53687091200, + "price": "$0", + "period": "year" + }, + { + "id": "200gb_yearly_v4", + "androidID": "200gb_yearly_v4", + "iosID": "200gb_yearly_v4", + "stripeID": "200gb_yearly_v4", + "storage": 214748364800, + "price": "$0", + "period": "year" + }, + { + "id": "1000gb_yearly_v4", + "androidID": "1000gb_yearly_v4", + "iosID": "1000gb_yearly_v4", + "stripeID": "1000gb_yearly_v4", + "storage": 1073741824000, + "price": "$0", + "period": "year" + }, + { + "id": "2000gb_yearly_v4", + "androidID": "2000gb_yearly_v4", + "iosID": "2000gb_yearly_v4", + "stripeID": "2000gb_yearly_v4", + "storage": 2147483648000, + "price": "$0", + "period": "year" + }, + { + "id": "family", + "androidID": "family", + "iosID": "family", + "stripeID": "family", + "storage": 2147483648000, + "price": "$0", + "period": "year" + }, + { + "id": "free", + "androidID": "free", + "iosID": "free", + "stripeID": "free", + "storage": 10737418240, + "price": "$0", + "period": "year" + } + ], + "EU": [ + { + "id": "50gb_monthly_v4", + "androidID": "50gb_monthly_v4", + "iosID": "50gb_monthly_v4", + "stripeID": "50gb_monthly_v4", + "storage": 53687091200, + "price": "€0", + "period": "month" + }, + { + "id": "200gb_monthly_v4", + "androidID": "200gb_monthly_v4", + "iosID": "200gb_monthly_v4", + "stripeID": "200gb_monthly_v4", + "storage": 214748364800, + "price": "€0", + "period": "month" + }, + { + "id": "1000gb_monthly_v4", + "androidID": "1000gb_monthly_v4", + "iosID": "1000gb_monthly_v4", + "stripeID": "1000gb_monthly_v4", + "storage": 1073741824000, + "price": "€0", + "period": "month" + }, + { + "id": "2000gb_monthly_v4", + "androidID": "2000gb_monthly_v4", + "iosID": "2000gb_monthly_v4", + "stripeID": "2000gb_monthly_v4", + "storage": 2147483648000, + "price": "€0", + "period": "month" + }, + { + "id": "50gb_yearly_v4", + "androidID": "50gb_yearly_v4", + "iosID": "50gb_yearly_v4", + "stripeID": "50gb_yearly_v4", + "storage": 53687091200, + "price": "€0", + "period": "year" + }, + { + "id": "200gb_yearly_v4", + "androidID": "200gb_yearly_v4", + "iosID": "200gb_yearly_v4", + "stripeID": "200gb_yearly_v4", + "storage": 214748364800, + "price": "€0", + "period": "year" + }, + { + "id": "1000gb_yearly_v4", + "androidID": "1000gb_yearly_v4", + "iosID": "1000gb_yearly_v4", + "stripeID": "1000gb_yearly_v4", + "storage": 1073741824000, + "price": "€0", + "period": "year" + }, + { + "id": "2000gb_yearly_v4", + "androidID": "2000gb_yearly_v4", + "iosID": "2000gb_yearly_v4", + "stripeID": "2000gb_yearly_v4", + "storage": 2147483648000, + "price": "€0", + "period": "year" + }, + { + "id": "family", + "androidID": "family", + "iosID": "family", + "stripeID": "family", + "storage": 2147483648000, + "price": "€0", + "period": "year" + }, + { + "id": "free", + "androidID": "free", + "iosID": "free", + "stripeID": "free", + "storage": 10737418240, + "price": "€0", + "period": "year" + } + ] +} diff --git a/patches/museum-family-link.patch b/patches/museum-family-link.patch new file mode 100644 index 0000000..d800ee7 --- /dev/null +++ b/patches/museum-family-link.patch @@ -0,0 +1,31 @@ +diff --git a/server/pkg/controller/family/admin.go b/server/pkg/controller/family/admin.go +index 1b58f6b8..8fd74a99 100644 +--- a/server/pkg/controller/family/admin.go ++++ b/server/pkg/controller/family/admin.go +@@ +- "github.com/ente-io/museum/pkg/utils/auth" +- "github.com/ente-io/museum/pkg/utils/billing" ++ "github.com/ente-io/museum/pkg/utils/auth" ++ "github.com/ente-io/museum/pkg/utils/billing" + emailUtil "github.com/ente-io/museum/pkg/utils/email" + "github.com/ente-io/stacktrace" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/sirupsen/logrus" ++ "github.com/spf13/viper" + ) +@@ +- FamilyPlainHost = "https://family.ente.io" ++ defaultFamilyHost = "https://family.ente.io" + ) ++ ++func familyInviteHost() string { ++ host := viper.GetString("apps.family") ++ if host != "" { ++ return host ++ } ++ return defaultFamilyHost ++} +@@ +- templateData["FamilyInviteLink"] = fmt.Sprintf("%s?inviteToken=%s", FamilyPlainHost, *inviteToken) ++ templateData["FamilyInviteLink"] = fmt.Sprintf("%s?inviteToken=%s", familyInviteHost(), *inviteToken) diff --git a/start.sh b/start.sh index 963c7c9..edac763 100755 --- a/start.sh +++ b/start.sh @@ -666,6 +666,7 @@ sync_dir "$APP_DIR/server/migrations" "$MUSEUM_RUNTIME_DIR/migrations" sync_dir "$APP_DIR/server/web-templates" "$MUSEUM_RUNTIME_DIR/web-templates" sync_dir "$APP_DIR/server/mail-templates" "$MUSEUM_RUNTIME_DIR/mail-templates" sync_dir "$APP_DIR/server/assets" "$MUSEUM_RUNTIME_DIR/assets" +sync_dir "$APP_DIR/data" "$MUSEUM_RUNTIME_DIR/data" if [ ! -x "$MUSEUM_BIN" ]; then log ERROR "Museum binary not found at $MUSEUM_BIN" @@ -894,6 +895,7 @@ if [ -d "$WEB_RUNTIME_DIR" ]; then log INFO "Rewriting frontend endpoints for local deployment" FRONTEND_REPLACEMENTS=( "ENTE_API_ORIGIN_PLACEHOLDER|$API_ORIGIN" + "ENTE_WEB_ENDPOINT_PLACEHOLDER|$PHOTOS_URL" "https://api.ente.io|$API_ORIGIN" "https://accounts.ente.io|$ACCOUNTS_URL" "https://auth.ente.io|$AUTH_URL"