Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da50bf4773 | |||
| 4290a33ba9 | |||
| 62b6f7f9ac | |||
| e3eb1b0491 | |||
| fc82e988e9 | |||
| 5068e12025 | |||
| 4ff99bee64 | |||
| fd028ca591 | |||
| fd60e4425b | |||
| 2d68e44208 | |||
| fdbb6d9a7a | |||
| fc7135d483 | |||
| e95fd0a705 | |||
| f27720d544 | |||
| 950481b6c7 | |||
| 4d66067d20 | |||
| 4081e89fdd | |||
| d69ab22967 | |||
| 2424a5ffc1 | |||
| 6fd3bde19a | |||
| 144f2b78d1 | |||
| d828bf3b8e | |||
| 3c8309dffd | |||
| 7f7ae4e8bf | |||
| 6289577898 | |||
| 8df2a3a621 | |||
| 192070ffae | |||
| e69166fc91 | |||
| f32919d436 | |||
| d345b2f460 | |||
| f4fd4fdf77 | |||
| defe47f78d | |||
| 5dbbb094b4 | |||
| 50a19a7908 | |||
| c00be35fc7 | |||
| b223843bcd | |||
| d32c366683 | |||
| f545b8d797 | |||
| 1244467afa | |||
| 17839a17df | |||
| aefea17f2f | |||
| 4811e0986e | |||
| 9709ebe265 | |||
| 71db4afae1 | |||
| bdcf96150f | |||
| 43cb685842 | |||
| ded9e1d174 | |||
| e093bfc571 | |||
| e329b54b8b | |||
| 20c0f80de0 | |||
| 2fac328b3c | |||
| b2767897b2 | |||
| 74331a7fe9 | |||
| 98431a35dc | |||
| 98ccff7af9 | |||
| 546fe4fe5d | |||
| 428b7f0ea3 | |||
| 4819bda8ad | |||
| 783ad628b3 | |||
| a73d2b4959 | |||
| 42c1374606 | |||
| 6546f26d52 | |||
| 9640e0d785 | |||
| 1568175962 | |||
| 1358aefb60 | |||
| 5c76451474 | |||
| 23c9581f7b | |||
| 64b7570cc6 | |||
| 6f6741dfb5 | |||
| 31def9585a | |||
| 042c156960 | |||
| 92f5c76955 | |||
| 956d39fca5 | |||
| 1c34047f75 | |||
| 12b486ace3 | |||
| 38f08c135e | |||
| 549b91ff22 | |||
| 6c1903b5a4 | |||
| ece40fe707 | |||
| 4baeaed265 | |||
| 65e88f4408 | |||
| 05a0b42b8e | |||
| ead577dfcc | |||
| 8b28d7eb39 | |||
| 5f1cf21ebb | |||
| aaf0dc0ca3 | |||
| 6050c4564a | |||
| 1f7de4085d | |||
| cf7865b5d1 | |||
| 4b7fb0fd9c | |||
| 192ebd0b5d | |||
| d775c2fb66 | |||
| d8a40880d8 | |||
| 789d7028b9 | |||
| f9c17035f7 | |||
| 3558003bf1 | |||
| c2a2fab3b0 | |||
| a8f2d13234 | |||
| acadfc5af4 | |||
| 47cfcfaf24 | |||
| 08fbcacb5c | |||
| cf41205607 | |||
| dddefb0f50 | |||
| 1d30b3d943 | |||
| fb0d4fd34f | |||
| 1fc7bcac62 | |||
| 528a6eed66 | |||
| d11f21f13f | |||
| d113138975 | |||
| 018f455ce8 | |||
| c2faaf16fb |
@@ -1,34 +0,0 @@
|
|||||||
# Ente Cloudron – Quick Guide
|
|
||||||
|
|
||||||
## Build
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/andreasdueren/ente-cloudron.git
|
|
||||||
cd ente-cloudron
|
|
||||||
|
|
||||||
cloudron build \
|
|
||||||
--set-build-service builder.docker.due.ren \
|
|
||||||
--build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \
|
|
||||||
--set-repository andreasdueren/ente-cloudron \
|
|
||||||
--tag 0.5.3
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install
|
|
||||||
```bash
|
|
||||||
cloudron install \
|
|
||||||
--location ente.due.ren \
|
|
||||||
--image andreasdueren/ente-cloudron:0.5.3
|
|
||||||
```
|
|
||||||
|
|
||||||
## After Install
|
|
||||||
1. **S3** – In Cloudron File Manager open `/app/data/config/s3.env`, fill in your endpoint/region/bucket/access/secret, then restart the app from the dashboard. Optional replication: add both `S3_SECONDARY_*` (second hot bucket) **and** `S3_COLD_*` (cold bucket) variables to mirror uploads across three independent buckets. Replication is only enabled when all three buckets are present. See Ente’s [object storage guide](https://ente.io/help/self-hosting/administration/object-storage) for example configs.
|
|
||||||
2. **Secondary hostnames** – During installation Cloudron now prompts for hostnames for the Accounts/Auth/Cast/Albums/Family web apps (powered by `httpPorts`). Ensure matching DNS records exist that point to the primary app domain. If you use Cloudron-managed DNS, those records are created automatically; otherwise create CNAME/A records such as `accounts.<app-domain> → <app-domain>`.
|
|
||||||
|
|
||||||
Once DNS propagates, use the dedicated hosts (defaults shown below — substitute the names you selected during install):
|
|
||||||
- `https://<app-host>` (the hostname you chose during install, main UI & uploads)
|
|
||||||
- `https://accounts.<app-domain>`
|
|
||||||
- `https://auth.<app-domain>`
|
|
||||||
- `https://cast.<app-domain>`
|
|
||||||
- `https://albums.<app-domain>`
|
|
||||||
- `https://family.<app-domain>`
|
|
||||||
|
|
||||||
Check `cloudron logs --app ente.due.ren -f` or `/app/data/logs/startup.log` if anything looks off.
|
|
||||||
-106
@@ -1,111 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.5.7 (2025-11-20)
|
|
||||||
|
|
||||||
* Bundle the Ente Families web app so `family.<domain>` 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.
|
|
||||||
* Fix passkey enrollment on the accounts host by ensuring only the photos domain matches the `/api` proxy block.
|
|
||||||
|
|
||||||
## 0.5.6 (2025-11-18)
|
|
||||||
|
|
||||||
* Allow the accounts frontend origin in Museum’s `webauthn.rporigins` when subdomain routing is enabled so passkey enrollment via the desktop flow succeeds
|
|
||||||
* Document the Ente desktop scheme (`ente://app`) in the recommended S3 CORS rules to keep signed URL fetches working for the desktop client
|
|
||||||
* Add full three-bucket replication support (hot primary, hot secondary, cold tier) and test the workflow with Backblaze (primary hot), Hetzner (secondary hot), and Scaleway Glacier (cold)
|
|
||||||
* Note that the cold bucket must accept the GLACIER storage class—point the `S3_COLD_*` variables at a provider that supports it, or enable `are_local_buckets`/`use_path_style_urls` so the start script switches Museum into local-bucket mode and skips the Glacier storage class entirely
|
|
||||||
|
|
||||||
## 0.5.5 (2025-11-18)
|
|
||||||
|
|
||||||
* Validate S3 data-center identifiers so replication only uses the canonical `b2-eu-cen`/`wasabi-eu-central-2-v3`/`scw-eu-fr-v3` keys and update the docs to reflect the upstream requirements
|
|
||||||
* Inject the API origin into all served HTML so the Next.js bundles (including `accounts/passkeys`) read the self-hosted endpoint instead of defaulting to `https://api.ente.io`
|
|
||||||
* Document the working Backblaze B2 CORS JSON that whitelists the wildcard origin + upload operations for desktop casts
|
|
||||||
|
|
||||||
## 0.5.4 (2025-11-18)
|
|
||||||
|
|
||||||
* Respect user-defined S3 data-center identifiers so replication targets use the intended buckets
|
|
||||||
|
|
||||||
## 0.5.1 (2025-11-05)
|
|
||||||
|
|
||||||
* Fix `httpPorts` host detection so accounts/cast/family/albums subdomains serve their static frontends again
|
|
||||||
|
|
||||||
## 0.5.2 (2025-11-05)
|
|
||||||
|
|
||||||
* Allow httpPort hostnames like `cast.ente`/`accounts.ente` so Cloudron can append the primary domain (`.due.ren`) automatically
|
|
||||||
|
|
||||||
## 0.5.3 (2025-11-05)
|
|
||||||
|
|
||||||
* Fix regression that could produce duplicated suffixes (e.g. `cast.due.due.ren`) when httpPort hostnames already included the full domain
|
|
||||||
|
|
||||||
## 0.5.0 (2025-11-04)
|
|
||||||
|
|
||||||
* Proxy Museum GET/HEAD routes (e.g. `/collections`, `/files`, `/remote-store`) so clients that talk to the primary host without `/api` still hit the backend
|
|
||||||
|
|
||||||
## 0.4.13 (2025-11-04)
|
|
||||||
|
|
||||||
* Forward all non-GET requests to the Museum backend so uploads and other write operations reach the API
|
|
||||||
|
|
||||||
## 0.4.12 (2025-11-04)
|
|
||||||
|
|
||||||
* Ensure dedicated hosts serve static `.html` exports and SPA fallbacks (`/gallery`, etc.) without 404s
|
|
||||||
|
|
||||||
## 0.4.11 (2025-11-04)
|
|
||||||
|
|
||||||
* Serve static `.html` exports for all dedicated hosts so routes like `/gallery` refresh and upload flows work again
|
|
||||||
|
|
||||||
## 0.4.10 (2025-11-04)
|
|
||||||
|
|
||||||
* Fix SPA fallbacks on dedicated hosts so `/gallery` and other client routes refresh correctly
|
|
||||||
|
|
||||||
## 0.4.9 (2025-11-04)
|
|
||||||
|
|
||||||
* Raise default memory allocation to 3 GiB for smoother media processing workloads
|
|
||||||
|
|
||||||
## 0.4.8 (2025-11-04)
|
|
||||||
|
|
||||||
* Allow persistent Museum overrides via `/app/data/config/museum.override.yaml` while keeping generated defaults intact
|
|
||||||
|
|
||||||
## 0.4.7 (2025-11-04)
|
|
||||||
|
|
||||||
* Proxy `/users` API endpoints through Caddy so mobile SRP/OTT flows reach the backend
|
|
||||||
|
|
||||||
## 0.4.6 (2025-11-04)
|
|
||||||
|
|
||||||
* Switch to Cloudron `httpPorts` so secondary web apps get provisioned domains automatically
|
|
||||||
* Teach the startup script to honour Cloudron-provided secondary domain variables (no manual aliasing required)
|
|
||||||
* Refresh post-install docs and build instructions to reflect the new installation flow
|
|
||||||
|
|
||||||
## 0.4.5 (2025-10-30)
|
|
||||||
|
|
||||||
* Serve photos UI on the primary hostname and mount other apps on `accounts/auth/cast/albums/family.<app-domain>`
|
|
||||||
* Enable multiDomain in the manifest so aliases can be set in Cloudron UI
|
|
||||||
* Simplified documentation for S3 setup and alias domains
|
|
||||||
* Fix CORS responses for auth subdomains and forward real client IPs from Cloudron proxy
|
|
||||||
* Remove unsupported Caddy `trusted_proxies` stanza while continuing to trust Cloudron-provided `X-Forwarded-For` headers for accurate logging
|
|
||||||
* Set CORS headers via reverse proxy response rewrites so cross-subdomain logins work reliably
|
|
||||||
|
|
||||||
## 0.4.4 (2025-10-30)
|
|
||||||
|
|
||||||
* Restore Cloudflare R2 path-style URLs and simplify to a single hot-storage data center
|
|
||||||
* Serve the frontend apps on dedicated subdomains (photos/accounts/auth/cast/albums/family)
|
|
||||||
* Startup script now regenerates Caddy and Museum configs for the new host layout
|
|
||||||
* Added post-install checklist entries and updated docs for required DNS records
|
|
||||||
|
|
||||||
## 0.4.3 (2025-10-29)
|
|
||||||
|
|
||||||
* Always regenerate Museum configuration on startup to pick up S3 credential changes
|
|
||||||
* Enables seamless workflow: add S3 credentials to /app/data/config/s3.env and restart
|
|
||||||
* Fixes issue where S3 configuration changes required manual intervention
|
|
||||||
|
|
||||||
## 0.4.2 (2025-10-29)
|
|
||||||
|
|
||||||
* Use SMTPS (port 2465) with TLS encryption for email delivery
|
|
||||||
* Fixes email sending with requiresValidCertificate flag on Cloudron 9
|
|
||||||
|
|
||||||
## 0.4.1 (2025-10-23)
|
|
||||||
|
|
||||||
* Fix email sending for user registration by enabling TLS certificate validation in sendmail addon
|
|
||||||
* Add requiresValidCertificate flag to sendmail configuration to ensure proper SMTP authentication with Go applications
|
|
||||||
* Note: Requires Cloudron 9 or later for requiresValidCertificate support
|
|
||||||
|
|
||||||
## 1.0.0 (2024-06-01)
|
## 1.0.0 (2024-06-01)
|
||||||
|
|
||||||
* Initial release of Ente for Cloudron
|
* Initial release of Ente for Cloudron
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
Cloudron Application Packaging System Prompt
|
|
||||||
|
|
||||||
You are a Cloudron packaging expert specializing in creating complete, production-ready Cloudron packages. When a user requests packaging an application, follow this comprehensive process:
|
|
||||||
|
|
||||||
Core Process
|
|
||||||
|
|
||||||
1. Application Research: Research the target application's architecture, dependencies, configuration requirements, and deployment patterns
|
|
||||||
2. Package Generation: Create all required Cloudron packaging files
|
|
||||||
3. Documentation: Provide build and deployment instructions
|
|
||||||
|
|
||||||
Required Files to Generate
|
|
||||||
|
|
||||||
CloudronManifest.json
|
|
||||||
|
|
||||||
- Use reverse-domain notation for app ID (e.g., io.example.appname)
|
|
||||||
- Configure memory limits based on application requirements (minimum 128MB)
|
|
||||||
- Set httpPort matching NGINX configuration
|
|
||||||
- Include necessary addons: postgresql, mysql, mongodb, redis, localstorage, sendmail
|
|
||||||
- Add complete metadata: title, description, author, website, contactEmail
|
|
||||||
- Configure authentication: oidc (preferred) or ldap
|
|
||||||
- Include postInstallMessage with login credentials if applicable
|
|
||||||
- Add health check endpoints
|
|
||||||
- Set proper minBoxVersion (typically "7.0.0")
|
|
||||||
|
|
||||||
Dockerfile
|
|
||||||
|
|
||||||
- Base image: FROM cloudron/base:5.0.0
|
|
||||||
- Cloudron filesystem structure:
|
|
||||||
- /app/code - application code (read-only)
|
|
||||||
- /app/data - persistent data (backed up)
|
|
||||||
- /tmp - temporary files
|
|
||||||
- /run - runtime files
|
|
||||||
- Install dependencies and application
|
|
||||||
- Copy initialization data to /tmp/data
|
|
||||||
- Set proper permissions and ownership
|
|
||||||
- Configure services to log to stdout/stderr
|
|
||||||
- Entry point: CMD ["/app/code/start.sh"]
|
|
||||||
|
|
||||||
start.sh
|
|
||||||
|
|
||||||
- Initialize /app/data from /tmp/data on first run
|
|
||||||
- Configure application using Cloudron environment variables
|
|
||||||
- Handle addon configurations (database connections, etc.)
|
|
||||||
- Generate secrets/API keys on first run
|
|
||||||
- Set proper file permissions (chown cloudron:cloudron)
|
|
||||||
- Run database migrations if needed
|
|
||||||
- Configure authentication providers
|
|
||||||
- Launch application with supervisor or directly
|
|
||||||
|
|
||||||
NGINX Configuration
|
|
||||||
|
|
||||||
- Listen on port specified in CloudronManifest.json
|
|
||||||
- Handle proxy headers properly:
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
- Configure static file serving
|
|
||||||
- Set up authentication routes for OIDC callbacks
|
|
||||||
- Ensure logs go to stdout/stderr
|
|
||||||
|
|
||||||
Supervisor Configuration (if needed)
|
|
||||||
|
|
||||||
- Multiple process management
|
|
||||||
- Proper signal handling
|
|
||||||
- Run processes as cloudron user
|
|
||||||
- Configure log output to stdout/stderr
|
|
||||||
|
|
||||||
Authentication Integration
|
|
||||||
|
|
||||||
OIDC (Preferred)
|
|
||||||
|
|
||||||
- Environment variables: CLOUDRON_OIDC_IDENTIFIER, CLOUDRON_OIDC_CLIENT_ID, CLOUDRON_OIDC_CLIENT_SECRET
|
|
||||||
- Callback route: /api/v1/session/callback
|
|
||||||
- User provisioning and group mapping
|
|
||||||
- Session management compatible with Cloudron proxy
|
|
||||||
|
|
||||||
LDAP (Fallback)
|
|
||||||
|
|
||||||
- Environment variables: CLOUDRON_LDAP_SERVER, CLOUDRON_LDAP_PORT, CLOUDRON_LDAP_BIND_DN, CLOUDRON_LDAP_BIND_PASSWORD
|
|
||||||
- User search base and group mapping
|
|
||||||
- Proper LDAP query configuration
|
|
||||||
|
|
||||||
Cloudron Environment Variables
|
|
||||||
|
|
||||||
Always utilize these standard variables:
|
|
||||||
- CLOUDRON_APP_ORIGIN - Application URL
|
|
||||||
- CLOUDRON_MAIL_SMTP_* - Email configuration
|
|
||||||
- Database addon variables (e.g., CLOUDRON_POSTGRESQL_URL)
|
|
||||||
- CLOUDRON_LDAP_* - LDAP configuration
|
|
||||||
- CLOUDRON_OIDC_* - OIDC configuration
|
|
||||||
|
|
||||||
Best Practices
|
|
||||||
|
|
||||||
1. Security: Never expose secrets, use environment variables
|
|
||||||
2. Persistence: Store data in /app/data, initialize from /tmp/data
|
|
||||||
3. Updates: Handle schema migrations and configuration updates
|
|
||||||
4. Logging: All logs to stdout/stderr for Cloudron log aggregation
|
|
||||||
5. Health Checks: Implement endpoints for monitoring
|
|
||||||
6. Process Management: Use supervisor for multi-process applications
|
|
||||||
7. File Permissions: Ensure cloudron user can read/write necessary files
|
|
||||||
8. Building: use the cloudron build service under builder.docker.due.ren
|
|
||||||
9. Installation: always uninstall and install fresh, never update an app during development
|
|
||||||
|
|
||||||
Build Instructions Format
|
|
||||||
|
|
||||||
Create a markdown file with:
|
|
||||||
- Prerequisites and dependencies
|
|
||||||
- Build commands (cloudron build, cloudron install)
|
|
||||||
- Testing procedures
|
|
||||||
- Deployment steps
|
|
||||||
- Troubleshooting common issues
|
|
||||||
- Configuration examples
|
|
||||||
|
|
||||||
Documentation References
|
|
||||||
|
|
||||||
- Cloudron CLI: https://docs.cloudron.io/packaging/cli/
|
|
||||||
- Packaging Tutorial: https://docs.cloudron.io/packaging/tutorial/
|
|
||||||
- Manifest Reference: https://docs.cloudron.io/packaging/manifest/
|
|
||||||
- Addons Guide: https://docs.cloudron.io/packaging/addons/
|
|
||||||
|
|
||||||
Viewing logs
|
|
||||||
|
|
||||||
To view the logs of an app, use the logs command:
|
|
||||||
```cloudron logs --app blog.example.com```
|
|
||||||
```cloudron logs --app 52aae895-5b7d-4625-8d4c-52980248ac21```
|
|
||||||
Pass the -f to follow the logs. Note that not all apps log to stdout/stderr. For this reason, you may need to look further in the file system for logs:
|
|
||||||
```cloudron exec --app blog.example.com # shell into the app's file system```
|
|
||||||
``# tail -f /run/wordpress/wp-debug.log # note that log file path and name is specific to the app```
|
|
||||||
|
|
||||||
|
|
||||||
When packaging an application, research thoroughly, create production-ready configurations, and provide comprehensive documentation for successful deployment.
|
|
||||||
|
|
||||||
Always Build with the build service (switch out name and version) build with cloudron build --set-build-service builder.docker.due.ren --build-service-token
|
|
||||||
e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e --set-repository andreasdueren/ente-cloudron --tag 0.1.0
|
|
||||||
|
|
||||||
cloudron install --location ente.due.ren --image andreasdueren/ente-cloudron:0.1.0
|
|
||||||
|
|
||||||
After install and build, don’t wait more than 30 seconds for feedback. When there is an error during install, this will not finish and you will wait forever.
|
|
||||||
|
|
||||||
Remember all of this crucial information throughout the packaging process. Create a file for persistency if necessary to poll from later.
Fix this packaging of ente for cloudron:
|
|
||||||
|
|
||||||
https://github.com/ente-io/ente/tree/main
|
|
||||||
|
|
||||||
There is documentation about self-hosting here: https://github.com/ente-io/ente/tree/main/docs/docs/self-hosting and here https://github.com/ente-io/ente/tree/main/server
|
|
||||||
|
|
||||||
Use Caddy as a reverse proxy. More info on setting it up: https://help.ente.io/self-hosting/reverse-proxy
|
|
||||||
|
|
||||||
Set up all web-apps (public-albums, cast, accounts, family). Use a path (/albums, /cast…) and not sub domains.: https://help.ente.io/self-hosting/museum
|
|
||||||
|
|
||||||
|
|
||||||
Stick to the original maintainers setup as close as possible while adhering to cordons restricti0ns. Use cloudrons postgresql as a database and an external s3 instance for object storage. You can use the following credentials for development but never commit these to any repository:
|
|
||||||
primary-storage:
|
|
||||||
key: "bbdfcc78c3d8aa970498fc309f1e5876" # Your S3 access key
|
|
||||||
secret: "4969ba66f326b4b7af7ca69716ee4a16931725a351a93643efce6447f81c9d68" # Your S3 secret key
|
|
||||||
endpoint: "40db7844966a4e896ccfac20ac9e7fb5.r2.cloudflarestorage.com" # S3 endpoint URL
|
|
||||||
region: "wnam" # S3 region (e.g. us-east-1)
|
|
||||||
bucket: "ente-due-ren" # Your bucket name
|
|
||||||
Here are the instructions as to how to use an external s3: https://help.ente.io/self-hosting/guides/external-s3
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
admin off
|
|
||||||
auto_https off
|
|
||||||
}
|
|
||||||
|
|
||||||
:3080 {
|
|
||||||
log {
|
|
||||||
output stdout
|
|
||||||
level DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
# Simple health check that always works
|
|
||||||
handle /health {
|
|
||||||
respond "{\"status\": \"OK\"}" 200
|
|
||||||
}
|
|
||||||
|
|
||||||
# Catch-all for debugging
|
|
||||||
handle {
|
|
||||||
respond "Caddy is running on port 3080" 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+16
-74
@@ -1,96 +1,38 @@
|
|||||||
{
|
{
|
||||||
"id": "io.ente.cloudronapp",
|
"id": "io.ente.cloudronapp",
|
||||||
"title": "Ente",
|
"title": "Ente",
|
||||||
"author": "Ente Development Team",
|
"author": "Ente Authors",
|
||||||
"description": "file://DESCRIPTION.md",
|
"description": "file://DESCRIPTION.md",
|
||||||
"changelog": "file://CHANGELOG.md",
|
"changelog": "file://CHANGELOG.md",
|
||||||
"contactEmail": "contact@ente.io",
|
"contactEmail": "contact@ente.io",
|
||||||
"website": "https://ente.io",
|
"tagline": "Open Source End-to-End Encrypted Photos & Authentication",
|
||||||
"tagline": "Open source, end-to-end encrypted photo backup",
|
"upstreamVersion": "1.0.0",
|
||||||
"version": "0.5.6",
|
"version": "0.1.62",
|
||||||
"upstreamVersion": "git-main",
|
"healthCheckPath": "/ping",
|
||||||
"healthCheckPath": "/health",
|
|
||||||
"httpPort": 3080,
|
"httpPort": 3080,
|
||||||
"httpPorts": {
|
"memoryLimit": 1073741824,
|
||||||
"ACCOUNTS_DOMAIN": {
|
|
||||||
"title": "Accounts hostname",
|
|
||||||
"description": "Hostname for the Ente accounts web app (e.g. accounts)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "accounts",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"AUTH_DOMAIN": {
|
|
||||||
"title": "Auth hostname",
|
|
||||||
"description": "Hostname for the Ente authentication frontend (e.g. auth)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "auth",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"CAST_DOMAIN": {
|
|
||||||
"title": "Cast hostname",
|
|
||||||
"description": "Hostname for the Ente casting web app (e.g. cast)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "cast",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"ALBUMS_DOMAIN": {
|
|
||||||
"title": "Public albums hostname",
|
|
||||||
"description": "Hostname for the Ente public albums frontend (e.g. albums)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "albums",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"SHARE_DOMAIN": {
|
|
||||||
"title": "Public locker hostname",
|
|
||||||
"description": "Hostname for the Ente share/collaboration frontend (e.g. share)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "share",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"EMBED_DOMAIN": {
|
|
||||||
"title": "Embed hostname",
|
|
||||||
"description": "Hostname for the Ente embed frontend (e.g. embed)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "embed",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"PAYMENTS_DOMAIN": {
|
|
||||||
"title": "Payments hostname",
|
|
||||||
"description": "Hostname for the Ente payments frontend (e.g. payments)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "payments",
|
|
||||||
"aliasableDomain": true
|
|
||||||
},
|
|
||||||
"FAMILY_DOMAIN": {
|
|
||||||
"title": "Family hostname",
|
|
||||||
"description": "Hostname for the Ente family web app (e.g. family)",
|
|
||||||
"containerPort": 3080,
|
|
||||||
"defaultValue": "family",
|
|
||||||
"aliasableDomain": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"memoryLimit": 3221225472,
|
|
||||||
"postInstallMessage": "file://POSTINSTALL.md",
|
|
||||||
"addons": {
|
"addons": {
|
||||||
"localstorage": {},
|
"localstorage": {},
|
||||||
"postgresql": {},
|
"postgresql": {},
|
||||||
|
"email": {},
|
||||||
"sendmail": {
|
"sendmail": {
|
||||||
"supportsDisplayName": true,
|
"supportsDisplayName": true
|
||||||
"requiresValidCertificate": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"checklist": {
|
"checklist": {
|
||||||
"configure-object-storage": {
|
"create-permanent-admin": {
|
||||||
"message": "Configure your S3-compatible storage in /app/data/config/s3.env before first use."
|
"message": "Required: S3 Storage Configuration!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"icon": "file://logo.png",
|
"icon": "file://logo.png",
|
||||||
"tags": [
|
"tags": [
|
||||||
"photos",
|
"photos",
|
||||||
"encryption",
|
"authentication",
|
||||||
"backup",
|
"e2ee",
|
||||||
"self-hosting"
|
"encryption"
|
||||||
],
|
],
|
||||||
"manifestVersion": 2,
|
"manifestVersion": 2,
|
||||||
"minBoxVersion": "8.1.0"
|
"minBoxVersion": "8.1.0",
|
||||||
|
"website": "https://ente.io"
|
||||||
}
|
}
|
||||||
+149
-130
@@ -1,154 +1,173 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# Build Museum server from source
|
||||||
|
|
||||||
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 "${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
|
FROM golang:1.24-bookworm AS museum-builder
|
||||||
COPY --from=ente-source /src /ente
|
|
||||||
|
WORKDIR /ente
|
||||||
|
|
||||||
|
# Clone the repository for server building
|
||||||
|
RUN apt-get update && apt-get install -y git libsodium-dev && \
|
||||||
|
git clone --depth=1 https://github.com/ente-io/ente.git . && \
|
||||||
|
apt-get clean && apt-get autoremove && \
|
||||||
|
rm -rf /var/cache/apt /var/lib/apt/lists
|
||||||
|
|
||||||
|
# Build the Museum server
|
||||||
WORKDIR /ente/server
|
WORKDIR /ente/server
|
||||||
RUN apt-get update && \
|
RUN go mod download && \
|
||||||
apt-get install -y --no-install-recommends build-essential pkg-config libsodium-dev && \
|
CGO_ENABLED=1 GOOS=linux go build -a -o museum ./cmd/museum
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
RUN mkdir -p /build/museum && \
|
|
||||||
CGO_ENABLED=1 GOOS=linux go build -o /build/museum/museum ./cmd/museum && \
|
|
||||||
for dir in migrations web-templates mail-templates assets; do \
|
|
||||||
rm -rf "/build/museum/$dir"; \
|
|
||||||
if [ -d "$dir" ]; then \
|
|
||||||
cp -r "$dir" "/build/museum/$dir"; \
|
|
||||||
else \
|
|
||||||
mkdir -p "/build/museum/$dir"; \
|
|
||||||
fi; \
|
|
||||||
done
|
|
||||||
|
|
||||||
FROM golang:1.24-bookworm AS cli-builder
|
FROM node:20-bookworm-slim as web-builder
|
||||||
COPY --from=ente-source /src /ente
|
|
||||||
WORKDIR /ente/cli
|
|
||||||
RUN go build -o /build/ente .
|
|
||||||
|
|
||||||
FROM node:20-bookworm-slim AS web-builder
|
WORKDIR /ente
|
||||||
ENV NEXT_PUBLIC_ENTE_ENDPOINT=ENTE_API_ORIGIN_PLACEHOLDER
|
|
||||||
ENV NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=https://albums.localhost.invalid
|
# Clone the repository for web app building
|
||||||
COPY --from=ente-source /src /ente
|
RUN apt-get update && apt-get install -y git && \
|
||||||
WORKDIR /ente/web
|
git clone --depth=1 https://github.com/ente-io/ente.git . && \
|
||||||
RUN apt-get update && \
|
apt-get clean && apt-get autoremove && \
|
||||||
apt-get install -y --no-install-recommends build-essential python3 && \
|
rm -rf /var/cache/apt /var/lib/apt/lists
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
# Will help default to yarn version 1.22.22
|
||||||
RUN corepack enable
|
RUN corepack enable
|
||||||
RUN yarn install --network-timeout 1000000
|
|
||||||
RUN mkdir -p /build/web/photos /build/web/accounts /build/web/auth /build/web/cast /build/web/albums /build/web/family /build/web/share /build/web/embed /build/web/payments
|
# Set environment variables for web app build
|
||||||
RUN set -e; \
|
# Set the API endpoint to use current origin - this will work at runtime
|
||||||
yarn build:photos; \
|
ENV NEXT_PUBLIC_ENTE_ENDPOINT="https://example.com/api"
|
||||||
yarn build:accounts; \
|
# Add a note for clarity
|
||||||
yarn build:auth; \
|
RUN echo "Building with placeholder NEXT_PUBLIC_ENTE_ENDPOINT, will be served by Caddy proxy at /api"
|
||||||
yarn build:cast; \
|
|
||||||
yarn build:share; \
|
# Debugging the repository structure
|
||||||
yarn build:embed; \
|
RUN find . -type d -maxdepth 3 | sort
|
||||||
yarn build:payments
|
|
||||||
RUN if [ -d "apps" ]; then \
|
# Check if web directory exists with apps subdirectory
|
||||||
for app in photos accounts auth cast share embed payments; do \
|
RUN mkdir -p /build/web/photos /build/web/accounts /build/web/auth /build/web/cast && \
|
||||||
if [ -d "apps/${app}/out" ]; then \
|
if [ -d "web" ] && [ -d "web/apps" ]; then \
|
||||||
rm -rf "/build/web/${app}"; \
|
echo "Found web/apps directory, building web apps"; \
|
||||||
mkdir -p "/build/web/${app}"; \
|
cd web && \
|
||||||
cp -r "apps/${app}/out/." "/build/web/${app}/"; \
|
yarn cache clean && \
|
||||||
else \
|
yarn install --network-timeout 1000000000 && \
|
||||||
printf 'Missing build output for %s\n' "${app}"; \
|
yarn build:photos && \
|
||||||
printf '<html><body><h1>Ente %s</h1><p>Build output missing.</p></body></html>\n' "${app}" > "/build/web/${app}/index.html"; \
|
yarn build:accounts && \
|
||||||
fi; \
|
yarn build:auth && \
|
||||||
done; \
|
yarn build:cast && \
|
||||||
else \
|
if [ -d "apps/photos/out" ]; then \
|
||||||
for app in photos accounts auth cast; do \
|
cp -r apps/photos/out/* /build/web/photos/; \
|
||||||
printf '<html><body><h1>Ente %s</h1><p>Build output missing.</p></body></html>\n' "${app}" > "/build/web/${app}/index.html"; \
|
|
||||||
done; \
|
|
||||||
fi && \
|
fi && \
|
||||||
rm -rf /build/web/albums && \
|
if [ -d "apps/accounts/out" ]; then \
|
||||||
cp -r /build/web/photos /build/web/albums
|
cp -r apps/accounts/out/* /build/web/accounts/; \
|
||||||
|
fi && \
|
||||||
FROM node:20-bookworm-slim AS families-builder
|
if [ -d "apps/auth/out" ]; then \
|
||||||
ENV NEXT_PUBLIC_ENTE_ENDPOINT=ENTE_API_ORIGIN_PLACEHOLDER \
|
cp -r apps/auth/out/* /build/web/auth/; \
|
||||||
NEXT_WEB_ENTE_ENDPOINT=ENTE_WEB_ENDPOINT_PLACEHOLDER \
|
fi && \
|
||||||
NEXT_PUBLIC_IS_SENTRY_ENABLED=no \
|
if [ -d "apps/cast/out" ]; then \
|
||||||
NEXT_PUBLIC_SENTRY_ENV=local \
|
cp -r apps/cast/out/* /build/web/cast/; \
|
||||||
NEXT_PUBLIC_SENTRY_DSN= \
|
fi; \
|
||||||
NEXT_TELEMETRY_DISABLED=1
|
elif [ -d "web" ]; then \
|
||||||
COPY --from=families-source /families /families
|
echo "Found web directory, looking for alternative structure"; \
|
||||||
WORKDIR /families
|
find web -type d | grep -v node_modules | sort; \
|
||||||
RUN apt-get update && \
|
if [ -d "web/photos" ]; then \
|
||||||
apt-get install -y --no-install-recommends build-essential python3 && \
|
echo "Building photos app"; \
|
||||||
rm -rf /var/lib/apt/lists/*
|
cd web/photos && yarn install && yarn build && \
|
||||||
RUN corepack enable
|
if [ -d "out" ]; then cp -r out/* /build/web/photos/; fi; \
|
||||||
RUN yarn install --network-timeout 1000000
|
fi; \
|
||||||
RUN mkdir -p /build/family && \
|
if [ -d "web/accounts" ]; then \
|
||||||
yarn build && \
|
echo "Building accounts app"; \
|
||||||
./node_modules/.bin/next export -o /build/family
|
cd web/accounts && yarn install && yarn build && \
|
||||||
|
if [ -d "out" ]; then cp -r out/* /build/web/accounts/; fi; \
|
||||||
|
fi; \
|
||||||
|
if [ -d "web/auth" ]; then \
|
||||||
|
echo "Building auth app"; \
|
||||||
|
cd web/auth && yarn install && yarn build && \
|
||||||
|
if [ -d "out" ]; then cp -r out/* /build/web/auth/; fi; \
|
||||||
|
fi; \
|
||||||
|
if [ -d "web/cast" ]; then \
|
||||||
|
echo "Building cast app"; \
|
||||||
|
cd web/cast && yarn install && yarn build && \
|
||||||
|
if [ -d "out" ]; then cp -r out/* /build/web/cast/; fi; \
|
||||||
|
fi; \
|
||||||
|
else \
|
||||||
|
echo "Web directory not found, creating placeholder web pages"; \
|
||||||
|
# Create placeholder HTML files for each app \
|
||||||
|
mkdir -p /build/web/photos /build/web/accounts /build/web/auth /build/web/cast; \
|
||||||
|
echo "<html><body><h1>Ente Photos</h1><p>Web app not available. Please check the build logs.</p></body></html>" > /build/web/photos/index.html; \
|
||||||
|
echo "<html><body><h1>Ente Accounts</h1><p>Web app not available. Please check the build logs.</p></body></html>" > /build/web/accounts/index.html; \
|
||||||
|
echo "<html><body><h1>Ente Auth</h1><p>Web app not available. Please check the build logs.</p></body></html>" > /build/web/auth/index.html; \
|
||||||
|
echo "<html><body><h1>Ente Cast</h1><p>Web app not available. Please check the build logs.</p></body></html>" > /build/web/cast/index.html; \
|
||||||
|
fi
|
||||||
|
|
||||||
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
|
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
|
||||||
|
|
||||||
ENV APP_DIR=/app/code \
|
# Install necessary packages and Caddy webserver
|
||||||
DATA_DIR=/app/data \
|
|
||||||
HOME=/app/data/home
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends ca-certificates curl jq libsodium23 pkg-config postgresql-client caddy openssl && \
|
apt-get install -y curl git nodejs npm libsodium23 libsodium-dev pkg-config postgresql-client && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
npm install -g yarn serve && \
|
||||||
|
# Install Caddy for web server
|
||||||
|
apt-get install -y debian-keyring debian-archive-keyring apt-transport-https && \
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y caddy && \
|
||||||
|
apt-get clean && apt-get autoremove && \
|
||||||
|
rm -rf /var/cache/apt /var/lib/apt/lists
|
||||||
|
|
||||||
RUN mkdir -p /app/pkg /app/web "$HOME" && chown -R cloudron:cloudron /app /app/web "$HOME"
|
# Install Go 1.24.1
|
||||||
|
RUN curl -L https://go.dev/dl/go1.24.1.linux-amd64.tar.gz -o go.tar.gz && \
|
||||||
|
rm -rf /usr/local/go && \
|
||||||
|
tar -C /usr/local -xzf go.tar.gz && \
|
||||||
|
rm go.tar.gz && \
|
||||||
|
ln -sf /usr/local/go/bin/go /usr/local/bin/go && \
|
||||||
|
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
|
||||||
|
|
||||||
COPY --from=ente-source /src ${APP_DIR}
|
# Set up directory structure
|
||||||
COPY data ${APP_DIR}/data
|
RUN mkdir -p /app/code /app/data/config /app/data/caddy /app/web
|
||||||
RUN rm -rf ${APP_DIR}/.git
|
|
||||||
|
|
||||||
RUN mkdir -p /app/museum-bin
|
WORKDIR /app/code
|
||||||
COPY --from=museum-builder /build/museum/museum /app/museum-bin/museum
|
|
||||||
COPY --from=museum-builder /build/museum/migrations ${APP_DIR}/server/migrations
|
|
||||||
COPY --from=museum-builder /build/museum/web-templates ${APP_DIR}/server/web-templates
|
|
||||||
COPY --from=museum-builder /build/museum/mail-templates ${APP_DIR}/server/mail-templates
|
|
||||||
COPY --from=museum-builder /build/museum/assets ${APP_DIR}/server/assets
|
|
||||||
RUN chmod +x /app/museum-bin/museum
|
|
||||||
|
|
||||||
COPY --from=cli-builder /build/ente /app/code/ente
|
# Clone the ente repository during build (for the Museum server)
|
||||||
RUN ln -sf /app/code/ente /usr/local/bin/ente && chmod +x /app/code/ente
|
RUN git clone --depth=1 https://github.com/ente-io/ente.git . && \
|
||||||
|
sed -i 's/go 1.23/go 1.24.1/' server/go.mod && \
|
||||||
|
mkdir -p /app/data/go && \
|
||||||
|
cp -r server/go.mod server/go.sum /app/data/go/ && \
|
||||||
|
chmod 777 /app/data/go/go.mod /app/data/go/go.sum
|
||||||
|
|
||||||
|
# Pre-download Go dependencies
|
||||||
|
RUN cd server && \
|
||||||
|
export GOMODCACHE="/app/data/go/pkg/mod" && \
|
||||||
|
export GOFLAGS="-modfile=/app/data/go/go.mod -mod=mod" && \
|
||||||
|
export GOTOOLCHAIN=local && \
|
||||||
|
export GO111MODULE=on && \
|
||||||
|
export GOSUMDB=off && \
|
||||||
|
mkdir -p /app/data/go/pkg/mod && \
|
||||||
|
chmod -R 777 /app/data/go && \
|
||||||
|
go mod download
|
||||||
|
|
||||||
|
# Set Go environment variables
|
||||||
|
ENV GOTOOLCHAIN=local
|
||||||
|
ENV GO111MODULE=on
|
||||||
|
ENV GOFLAGS="-modfile=/app/data/go/go.mod -mod=mod"
|
||||||
|
ENV PATH="/usr/local/go/bin:${PATH}"
|
||||||
|
ENV GOSUMDB=off
|
||||||
|
ENV GOMODCACHE="/app/data/go/pkg/mod"
|
||||||
|
|
||||||
|
# Copy the web app built files from the first stage
|
||||||
COPY --from=web-builder /build/web/photos /app/web/photos
|
COPY --from=web-builder /build/web/photos /app/web/photos
|
||||||
COPY --from=web-builder /build/web/accounts /app/web/accounts
|
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/auth /app/web/auth
|
||||||
COPY --from=web-builder /build/web/cast /app/web/cast
|
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/share /app/web/share
|
|
||||||
COPY --from=web-builder /build/web/embed /app/web/embed
|
|
||||||
COPY --from=web-builder /build/web/payments /app/web/payments
|
|
||||||
COPY --from=families-builder /build/family /app/web/family
|
|
||||||
|
|
||||||
COPY start.sh /app/pkg/start.sh
|
# Copy Museum server binary from builder stage to app directory (not data volume)
|
||||||
COPY admin-helper.sh /app/pkg/admin-helper.sh
|
RUN mkdir -p /app/museum-bin
|
||||||
COPY admin-helper-direct.sh /app/pkg/admin-helper-direct.sh
|
COPY --from=museum-builder /ente/server/museum /app/museum-bin/museum
|
||||||
|
RUN chmod +x /app/museum-bin/museum
|
||||||
|
|
||||||
RUN chmod +x /app/pkg/start.sh /app/pkg/admin-helper.sh /app/pkg/admin-helper-direct.sh
|
# Copy configuration and startup scripts
|
||||||
RUN ln -s /app/data/cli-data /cli-data && \
|
ADD start.sh /app/pkg/
|
||||||
rm -rf /home/cloudron && \
|
ADD config.template.yaml /app/pkg/
|
||||||
ln -s /app/data/home /home/cloudron
|
|
||||||
|
|
||||||
EXPOSE 3080 8080
|
# Set proper permissions
|
||||||
|
RUN chmod +x /app/pkg/start.sh
|
||||||
|
|
||||||
|
# Expose the web port (Cloudron expects port 3080)
|
||||||
|
EXPOSE 3080
|
||||||
|
# Also expose API port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Start the application
|
||||||
CMD ["/app/pkg/start.sh"]
|
CMD ["/app/pkg/start.sh"]
|
||||||
+18
-83
@@ -1,90 +1,25 @@
|
|||||||
Your Ente installation is almost ready!
|
Your Ente installation is almost ready!
|
||||||
|
|
||||||
## Required: External Object Storage
|
## Required: S3 Storage Configuration
|
||||||
|
|
||||||
Before using Ente, configure an S3-compatible object storage provider:
|
Before you can use Ente, you need to configure an S3-compatible storage service:
|
||||||
|
|
||||||
1. Open the Cloudron dashboard and select your Ente app.
|
1. Go to your Cloudron dashboard
|
||||||
2. Launch the file explorer.
|
2. Click on your Ente app
|
||||||
3. Open `/app/data/config/s3.env` and provide values for **all** required keys.
|
3. Click on "Terminal"
|
||||||
4. Save the file and restart the app from the Cloudron dashboard.
|
4. Edit the S3 configuration template:
|
||||||
5. (Required for cast/slideshow) Configure your S3 bucket’s CORS policy to allow the Ente domains you serve from Cloudron (e.g. `https://ente.due.ren`, `https://accounts.due.ren`, `https://cast.due.ren`, etc.). Without CORS, browsers will block the signed URLs that power the cast slideshow.
|
|
||||||
- **Backblaze B2 tip:** B2 ships with “native” CORS rules that block S3-style updates. Install the Backblaze CLI `pip install 'b2<4'`, then:
|
|
||||||
```bash
|
|
||||||
# Authorise once (replace with your key ID/secret)
|
|
||||||
b2 authorize-account <KEY_ID> <APP_KEY>
|
|
||||||
|
|
||||||
# Inspect the current bucket type (usually allPrivate) and capture it
|
|
||||||
BUCKET_TYPE=$(b2 get-bucket ente-due-ren | awk -F'"' '/bucketType/ {print $4}')
|
|
||||||
|
|
||||||
# Clear any native rules without changing visibility
|
|
||||||
b2 update-bucket ente-due-ren "$BUCKET_TYPE" --cors-rules '[]'
|
|
||||||
|
|
||||||
# Apply the S3-compatible rule (adjust origins as needed)
|
|
||||||
cat >cors.json <<'EOF'
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"corsRuleName": "entephotos",
|
|
||||||
"allowedOrigins": ["*"],
|
|
||||||
"allowedHeaders": ["*"],
|
|
||||||
"allowedOperations": [
|
|
||||||
"b2_download_file_by_id",
|
|
||||||
"b2_download_file_by_name",
|
|
||||||
"b2_upload_file",
|
|
||||||
"b2_upload_part",
|
|
||||||
"s3_get",
|
|
||||||
"s3_post",
|
|
||||||
"s3_put",
|
|
||||||
"s3_head"
|
|
||||||
],
|
|
||||||
"exposeHeaders": ["X-Amz-Request-Id","X-Amz-Id-2","ETag"],
|
|
||||||
"maxAgeSeconds": 3600
|
|
||||||
}
|
|
||||||
]
|
|
||||||
EOF
|
|
||||||
b2 update-bucket ente-due-ren "$BUCKET_TYPE" --cors-rules "$(<cors.json)"
|
|
||||||
```
|
```
|
||||||
Verify with `curl -I -H 'Origin: https://ente.due.ren' <signed-url>`; you should see `Access-Control-Allow-Origin`.
|
nano /app/data/config/s3.env.template
|
||||||
|
|
||||||
Supported variables (these map directly to the fields described in the upstream “Configuring Object Storage” documentation):
|
|
||||||
- `S3_ENDPOINT` (e.g. `https://<account>.r2.cloudflarestorage.com`)
|
|
||||||
- `S3_REGION`
|
|
||||||
- `S3_BUCKET`
|
|
||||||
- `S3_ACCESS_KEY`
|
|
||||||
- `S3_SECRET_KEY`
|
|
||||||
- `S3_PREFIX` (optional path prefix)
|
|
||||||
- `S3_ARE_LOCAL_BUCKETS` (set to `false` when your provider uses HTTPS “real” domains instead of MinIO-style LAN endpoints)
|
|
||||||
- `S3_FORCE_PATH_STYLE` (set to `true` for MinIO, Cloudflare R2, Backblaze, or any host that requires `https://host/bucket/object` URLs)
|
|
||||||
- `S3_PRIMARY_DC`, `S3_SECONDARY_DC`, `S3_COLD_DC`, `S3_DERIVED_DC` (advanced: pick from the canonical data-center identifiers listed in the upstream docs. The names are hard-coded in Museum; leave them at `b2-eu-cen`, `wasabi-eu-central-2-v3`, `scw-eu-fr-v3` unless you know you need one of the legacy aliases such as `scw-eu-fr`.)
|
|
||||||
- Optional replication: define **both** `S3_SECONDARY_*` and `S3_COLD_*` (endpoints, keys, secrets, optional prefixes, DC names) to mirror uploads to a second hot bucket and a third cold bucket. Replication is only enabled when all three buckets are configured; otherwise the app stays in single-bucket mode. See [Ente’s object storage guide](https://ente.io/help/self-hosting/administration/object-storage) for sample layouts and discussion of reliability.
|
|
||||||
|
|
||||||
You should never edit the generated `/app/data/museum/configurations/local.yaml` directly. If you need to append extra settings (for example, defining `internal.super-admins`), create `/app/data/config/museum.override.yaml` and add only the keys you want to override. Copying the entire sample `s3:` block from the docs into that file will erase the credentials that the package renders from `s3.env` and break replication.
|
|
||||||
|
|
||||||
## Required: Secondary Hostnames
|
|
||||||
|
|
||||||
The installer now asks for dedicated hostnames for the Auth/Accounts/Cast/Albums/Family web apps (via Cloudron `httpPorts`). If you manage DNS outside of Cloudron, create CNAME/A records such as `accounts.<app-domain>`, `auth.<app-domain>`, etc., pointing at the primary app domain. With Cloudron-managed DNS the records are created automatically.
|
|
||||||
|
|
||||||
## Administration
|
|
||||||
|
|
||||||
- **Grant yourself admin privileges**
|
|
||||||
1. Open the Cloudron dashboard → your Ente app → **File Manager**.
|
|
||||||
2. Navigate to `/app/data/config/` and open (or create) `museum.override.yaml`.
|
|
||||||
3. Add your email to the super-admin list:
|
|
||||||
```yaml
|
|
||||||
internal:
|
|
||||||
super-admins:
|
|
||||||
- you@example.com
|
|
||||||
```
|
```
|
||||||
4. Save the file and restart the app. The override is appended to Museum’s configuration on every start.
|
5. Fill in your S3 credentials (AWS S3, MinIO, DigitalOcean Spaces, etc.)
|
||||||
|
6. Save the file and rename it:
|
||||||
- **Sign in to the bundled CLI**
|
|
||||||
*The package now preconfigures the CLI (config: `/app/data/cli-data/config.yaml`, exports: `/app/data/cli-data/export`).*
|
|
||||||
From the Cloudron **Terminal** run:
|
|
||||||
```bash
|
|
||||||
# authenticate once (enter the OTP you receive by email)
|
|
||||||
sudo -u cloudron ente account add
|
|
||||||
|
|
||||||
# inspect available commands
|
|
||||||
sudo -u cloudron ente --help
|
|
||||||
```
|
```
|
||||||
After you’re signed in you can follow the upstream docs for tasks like increasing storage: see [user administration](https://ente.io/help/self-hosting/administration/users) and the [CLI reference](https://ente.io/help/self-hosting/administration/cli). The [object storage guide](https://ente.io/help/self-hosting/administration/object-storage) explains the reliability setup: fill out `S3_*`, `S3_SECONDARY_*`, and `S3_COLD_*` in `/app/data/config/s3.env`, and the package will automatically enable three-bucket replication when you restart (no extra toggle needed).
|
mv /app/data/config/s3.env.template /app/data/config/s3.env
|
||||||
|
```
|
||||||
|
7. Restart your Ente app from the Cloudron dashboard
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Once S3 is configured, visit your app URL to create an admin account
|
||||||
|
2. Configure your mobile apps to use your custom self-hosted server (Settings → Advanced → Custom Server)
|
||||||
|
3. Enjoy your private, end-to-end encrypted photo storage!
|
||||||
@@ -9,6 +9,28 @@ This repository contains the Cloudron packaging for [Ente](https://ente.io), an
|
|||||||
- Configured to use Cloudron's mail service for sending emails
|
- Configured to use Cloudron's mail service for sending emails
|
||||||
- Easy to deploy and manage through the Cloudron interface
|
- Easy to deploy and manage through the Cloudron interface
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Browser Compatibility
|
||||||
|
|
||||||
|
Ente uses modern web technologies for its end-to-end encryption:
|
||||||
|
|
||||||
|
- **WebAssembly**: Required for cryptographic operations
|
||||||
|
- **IndexedDB**: Required for client-side data storage
|
||||||
|
|
||||||
|
Most modern browsers support these features, but they may be blocked by:
|
||||||
|
- Browser privacy settings
|
||||||
|
- Content Security Policies
|
||||||
|
- Certain browser extensions
|
||||||
|
|
||||||
|
This package includes custom Caddy configuration with appropriate security headers to ensure these features work correctly.
|
||||||
|
|
||||||
|
### S3-Compatible Storage
|
||||||
|
|
||||||
|
Ente requires an S3-compatible object storage service. You can use:
|
||||||
|
- Cloudron's built-in object storage
|
||||||
|
- External services like AWS S3, Wasabi, or MinIO
|
||||||
|
|
||||||
## Building and Installing
|
## Building and Installing
|
||||||
|
|
||||||
### Option 1: Build and Install Manually
|
### Option 1: Build and Install Manually
|
||||||
@@ -51,95 +73,23 @@ The app is configured automatically using Cloudron's environment variables for:
|
|||||||
- SMTP mail service
|
- SMTP mail service
|
||||||
- App origin URL
|
- App origin URL
|
||||||
|
|
||||||
### Cloudron Admin Notes
|
### Additional Configuration
|
||||||
|
|
||||||
After installing on Cloudron remember to:
|
The package includes several enhancements to ensure proper functionality:
|
||||||
|
|
||||||
1. Open the File Manager for the app, edit `/app/data/config/s3.env`, and set the S3-compatible credentials that belong in `museum.yaml`. The upstream documentation expects the canonical keys `b2-eu-cen` (primary), `wasabi-eu-central-2-v3` (secondary) and `scw-eu-fr-v3` (cold); this package renders those blocks automatically from the environment variables below so you don’t have to touch the generated config. At minimum set `S3_ENDPOINT`, `S3_REGION`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, plus the optional `S3_PREFIX`. To enable replication you must also define **both** `S3_SECONDARY_*` and `S3_COLD_*` (endpoint, region, bucket, key, secret, optional prefix/DC overrides); after a restart the package will flip `replication.enabled` on your behalf when all three buckets are present. Advanced knobs from the documentation map to the following variables:
|
1. **Security Headers**: Custom Content-Security-Policy headers that allow WebAssembly and IndexedDB
|
||||||
- `S3_ARE_LOCAL_BUCKETS=false` toggles SSL/subdomain-style URLs (`are_local_buckets` in `museum.yaml`); leave it at `true` for MinIO-style setups.
|
2. **API Configuration**: Dynamic runtime configuration to ensure the frontend connects to the correct API endpoint
|
||||||
- `S3_FORCE_PATH_STYLE=true` translates to `use_path_style_urls=true` (required for R2/MinIO and most LAN storage).
|
3. **CORS Headers**: Proper CORS configuration for API access
|
||||||
- The data-center identifiers (`b2-eu-cen`, `wasabi-eu-central-2-v3`, `scw-eu-fr-v3`, etc.) are **hard-coded upstream**. Keep the defaults unless you know you are targeting one of the legacy names (as listed in the Ente docs). The start script will ignore unknown values to prevent replication from breaking with empty bucket names.
|
|
||||||
- Leave the generated `museum/configurations/local.yaml` alone—if you need to append extra settings, do so via `/app/data/config/museum.override.yaml` and only add the keys you actually want to change. Copy‑pasting the full sample `s3:` block from the docs will overwrite the generated credentials with blanks.
|
|
||||||
- If you are using Cloudflare R2 or another hosted S3 provider, configure your bucket’s CORS policy to allow the Ente frontends (e.g. `https://ente.due.ren`, `https://accounts.due.ren`, `https://cast.due.ren`, **and** the desktop scheme `ente://app`) so that cast/slideshow playback and the desktop client can fetch signed URLs directly from storage. Backblaze B2 also requires clearing its “native” CORS rules; see the script in `POSTINSTALL.md`. When using the Backblaze CLI remember to preserve your bucket visibility (`allPrivate` for most installs): run `b2 get-bucket <bucket>` to confirm the current type, then invoke `b2 update-bucket <bucket> <bucketType> --cors-rules "$(<cors.json)"` so you only touch the CORS block. A minimal rule that works with Ente’s signed URLs looks like:
|
|
||||||
```bash
|
|
||||||
cat <<'EOF' >cors.json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"corsRuleName": "entephotos",
|
|
||||||
"allowedOrigins": ["*"],
|
|
||||||
"allowedHeaders": ["*"],
|
|
||||||
"allowedOperations": [
|
|
||||||
"b2_download_file_by_id",
|
|
||||||
"b2_download_file_by_name",
|
|
||||||
"b2_upload_file",
|
|
||||||
"b2_upload_part",
|
|
||||||
"s3_get",
|
|
||||||
"s3_post",
|
|
||||||
"s3_put",
|
|
||||||
"s3_head"
|
|
||||||
],
|
|
||||||
"exposeHeaders": ["X-Amz-Request-Id","X-Amz-Id-2","ETag"],
|
|
||||||
"maxAgeSeconds": 3600
|
|
||||||
}
|
|
||||||
]
|
|
||||||
EOF
|
|
||||||
b2 update-bucket ente-due-ren allPrivate --cors-rules "$(<cors.json)"
|
|
||||||
```
|
|
||||||
Adjust the hostnames and bucket type as needed; afterwards verify with `curl -I -H 'Origin: https://cast.example.com' '<signed-url>'` and ensure `Access-Control-Allow-Origin` is present.
|
|
||||||
2. When prompted during installation, pick hostnames for the Accounts/Auth/Cast/Albums/Family web apps (they are exposed via Cloudron `httpPorts`). Ensure matching DNS records exist; Cloudron-managed DNS creates them automatically, otherwise point CNAME/A records such as `accounts.<app-domain>` at the primary hostname.
|
|
||||||
3. To persist tweaks to Museum (for example, seeding super-admin or whitelist entries), create `/app/data/config/museum.override.yaml`. Its contents are appended to the generated `museum/configurations/local.yaml` on every start, so you only need to declare the keys you want to override.
|
|
||||||
```yaml
|
|
||||||
# /app/data/config/museum.override.yaml
|
|
||||||
internal:
|
|
||||||
super-admins:
|
|
||||||
- admin@example.com
|
|
||||||
```
|
|
||||||
4. Use the bundled Ente CLI for admin tasks via `cloudron exec --app <location> -- sudo -u cloudron ente --help`. On a fresh install run the following once (initialises the CLI config, whitelists your admin, and clears the CLI DB):
|
|
||||||
```bash
|
|
||||||
cloudron exec --app ente.cloudron.io -- bash -lc \
|
|
||||||
'cat <<EOF >/cli-data/config.yaml
|
|
||||||
endpoint:
|
|
||||||
api: https://ente.cloudron.io/api
|
|
||||||
log:
|
|
||||||
http: false
|
|
||||||
EOF
|
|
||||||
mkdir -p /cli-data/export
|
|
||||||
chown cloudron:cloudron /cli-data /cli-data/config.yaml /cli-data/export
|
|
||||||
cat <<EOF >/app/data/config/museum.override.yaml
|
|
||||||
internal:
|
|
||||||
super-admins:
|
|
||||||
- admin@example.com
|
|
||||||
EOF
|
|
||||||
rm -f /cli-data/ente-cli.db
|
|
||||||
chown cloudron:cloudron /app/data/config/museum.override.yaml'
|
|
||||||
|
|
||||||
cloudron restart --app ente.cloudron.io
|
You need to manually set up and configure:
|
||||||
|
|
||||||
# add your account (respond to prompts with the OTP sent to your email)
|
- S3-compatible object storage
|
||||||
cloudron exec --app ente.cloudron.io -- sudo -u cloudron ente account add
|
|
||||||
```
|
|
||||||
Afterwards the usual admin commands work as documented. Example:
|
|
||||||
```bash
|
|
||||||
cloudron exec --app ente.cloudron.io -- sudo -u cloudron ente admin list-users --admin-user admin@example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
The main photos UI continues to live on the hostname you selected during installation.
|
|
||||||
|
|
||||||
### Object storage quick reference
|
|
||||||
|
|
||||||
The upstream documentation at [ente.io/help/self-hosting/administration/object-storage](https://ente.io/help/self-hosting/administration/object-storage) is written for bare-metal installs where you edit `museum.yaml` by hand. The Cloudron package wraps those steps so you only maintain `/app/data/config/s3.env`, but the same concepts apply:
|
|
||||||
|
|
||||||
- **Canonical bucket names.** Museum’s schema ships with `b2-eu-cen`, `wasabi-eu-central-2-v3`, and `scw-eu-fr-v3`. You can point those identifiers at any S3-compatible provider, but you cannot rename them—replication logic only understands the upstream keys (or their documented legacy aliases). Leave the defaults in `s3.env` and only change the credentials/endpoints under each key.
|
|
||||||
- **Three buckets for replication.** Replication only works when two “hot” buckets and one “cold” bucket are configured. Populate `S3_*`, `S3_SECONDARY_*`, and `S3_COLD_*`; once all three have endpoints/keys/secrets the package automatically writes the `replication.enabled: true` stanza.
|
|
||||||
- **Transport settings.** Set `S3_ARE_LOCAL_BUCKETS=true`/`false` and `S3_FORCE_PATH_STYLE=true` to mirror the documentation’s `are_local_buckets`/`use_path_style_urls` toggles when talking to MinIO, Cloudflare R2, or other providers that require path-style URLs over HTTPS.
|
|
||||||
- **CORS.** If browsers cannot upload/download because of CORS, apply the recommended JSON from the docs (or the Backblaze helper script in `POSTINSTALL.md`). Ensure `Content-MD5` is listed in `AllowedHeaders` for providers with allow-lists.
|
|
||||||
- **Do not overwrite the generated config.** Keep `/app/data/config/museum.override.yaml` minimal (only the keys you need). Dropping the example `s3:` block from the docs into that file will clear the generated credentials and replication will fail with “PutObjectInput.Bucket” errors.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Web Client
|
### Web Client
|
||||||
|
|
||||||
After installation, you can access the Ente web client at your app's URL. Create the first user and promote them to an administrator using the override file or upstream admin tooling as documented by Ente.
|
After installation, you can access the Ente web client at your app's URL. Create an admin account on first use.
|
||||||
|
|
||||||
### Mobile Apps
|
### Mobile Apps
|
||||||
|
|
||||||
@@ -157,6 +107,18 @@ To update to a newer version:
|
|||||||
cloudron update --app ente.yourdomain.com
|
cloudron update --app ente.yourdomain.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **"Failed to fetch" errors**: Check if your browser is blocking API requests to your domain
|
||||||
|
2. **WebAssembly errors**: Ensure your browser supports and allows WebAssembly (try using Chrome or Firefox)
|
||||||
|
3. **IndexedDB errors**: Make sure your browser allows IndexedDB (not in private/incognito mode)
|
||||||
|
|
||||||
|
For issues specific to the Cloudron packaging, please open an issue in this repository.
|
||||||
|
|
||||||
|
For issues with Ente itself, please refer to the [main Ente repository](https://github.com/ente-io/ente).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This Cloudron package is licensed under the same license as Ente (Apache 2.0).
|
This Cloudron package is licensed under the same license as Ente (Apache 2.0).
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Direct Database Admin Helper for Ente Cloudron
|
|
||||||
# This script directly updates the database for admin operations
|
|
||||||
|
|
||||||
# Function to update user subscription directly in database
|
|
||||||
update_subscription() {
|
|
||||||
local user_email="$1"
|
|
||||||
local storage_gb="$2"
|
|
||||||
local valid_days="$3"
|
|
||||||
|
|
||||||
if [ -z "$user_email" ] || [ -z "$storage_gb" ] || [ -z "$valid_days" ]; then
|
|
||||||
echo "Usage: $0 update-subscription <user-email> <storage-gb> <valid-days>"
|
|
||||||
echo "Example: $0 update-subscription user@example.com 100 365"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Updating subscription for: $user_email"
|
|
||||||
echo "Storage: ${storage_gb}GB"
|
|
||||||
echo "Valid for: ${valid_days} days"
|
|
||||||
|
|
||||||
# Convert GB to bytes (1 GB = 1073741824 bytes)
|
|
||||||
local storage_bytes=$((storage_gb * 1073741824))
|
|
||||||
|
|
||||||
# Calculate expiry timestamp (current time + valid_days)
|
|
||||||
local current_timestamp=$(date +%s)
|
|
||||||
local expiry_timestamp=$((current_timestamp + (valid_days * 86400)))
|
|
||||||
# Convert to microseconds for the database
|
|
||||||
local expiry_microseconds="${expiry_timestamp}000000"
|
|
||||||
|
|
||||||
# Update the database directly
|
|
||||||
PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \
|
|
||||||
-h "$CLOUDRON_POSTGRESQL_HOST" \
|
|
||||||
-p "$CLOUDRON_POSTGRESQL_PORT" \
|
|
||||||
-U "$CLOUDRON_POSTGRESQL_USERNAME" \
|
|
||||||
-d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF
|
|
||||||
-- Update user's storage and subscription
|
|
||||||
UPDATE users
|
|
||||||
SET
|
|
||||||
storage_bonus = $storage_bytes,
|
|
||||||
subscription_expiry = $expiry_microseconds
|
|
||||||
WHERE email = '$user_email';
|
|
||||||
|
|
||||||
-- Show the updated values
|
|
||||||
SELECT
|
|
||||||
email,
|
|
||||||
storage_bonus / 1073741824.0 as storage_gb,
|
|
||||||
to_timestamp(subscription_expiry / 1000000) as subscription_expires
|
|
||||||
FROM users
|
|
||||||
WHERE email = '$user_email';
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "✓ Subscription updated successfully"
|
|
||||||
else
|
|
||||||
echo "✗ Failed to update subscription"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to get user details
|
|
||||||
get_user_details() {
|
|
||||||
local user_email="$1"
|
|
||||||
|
|
||||||
if [ -z "$user_email" ]; then
|
|
||||||
echo "Usage: $0 get-user <user-email>"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \
|
|
||||||
-h "$CLOUDRON_POSTGRESQL_HOST" \
|
|
||||||
-p "$CLOUDRON_POSTGRESQL_PORT" \
|
|
||||||
-U "$CLOUDRON_POSTGRESQL_USERNAME" \
|
|
||||||
-d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF
|
|
||||||
SELECT
|
|
||||||
email,
|
|
||||||
storage_bonus / 1073741824.0 as storage_gb,
|
|
||||||
storage_consumed / 1073741824.0 as used_gb,
|
|
||||||
to_timestamp(subscription_expiry / 1000000) as subscription_expires,
|
|
||||||
CASE
|
|
||||||
WHEN subscription_expiry > (EXTRACT(EPOCH FROM NOW()) * 1000000) THEN 'Active'
|
|
||||||
ELSE 'Expired'
|
|
||||||
END as status
|
|
||||||
FROM users
|
|
||||||
WHERE email = '$user_email';
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to list all users
|
|
||||||
list_users() {
|
|
||||||
PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \
|
|
||||||
-h "$CLOUDRON_POSTGRESQL_HOST" \
|
|
||||||
-p "$CLOUDRON_POSTGRESQL_PORT" \
|
|
||||||
-U "$CLOUDRON_POSTGRESQL_USERNAME" \
|
|
||||||
-d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF
|
|
||||||
SELECT
|
|
||||||
email,
|
|
||||||
storage_bonus / 1073741824.0 as storage_gb,
|
|
||||||
storage_consumed / 1073741824.0 as used_gb,
|
|
||||||
to_timestamp(subscription_expiry / 1000000) as expires,
|
|
||||||
CASE
|
|
||||||
WHEN subscription_expiry > (EXTRACT(EPOCH FROM NOW()) * 1000000) THEN 'Active'
|
|
||||||
ELSE 'Expired'
|
|
||||||
END as status
|
|
||||||
FROM users
|
|
||||||
ORDER BY email;
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main command handler
|
|
||||||
case "$1" in
|
|
||||||
"update-subscription")
|
|
||||||
update_subscription "$2" "$3" "$4"
|
|
||||||
;;
|
|
||||||
"get-user")
|
|
||||||
get_user_details "$2"
|
|
||||||
;;
|
|
||||||
"list-users")
|
|
||||||
list_users
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Ente Direct Admin Helper"
|
|
||||||
echo ""
|
|
||||||
echo "Usage:"
|
|
||||||
echo " $0 update-subscription <user-email> <storage-gb> <valid-days>"
|
|
||||||
echo " $0 get-user <user-email>"
|
|
||||||
echo " $0 list-users"
|
|
||||||
echo ""
|
|
||||||
echo "Examples:"
|
|
||||||
echo " $0 update-subscription user@example.com 100 365"
|
|
||||||
echo " $0 get-user user@example.com"
|
|
||||||
echo " $0 list-users"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Ente Admin Helper Script for Cloudron
|
|
||||||
# This script simplifies admin operations in the Cloudron terminal
|
|
||||||
|
|
||||||
MUSEUM_BIN="/app/museum-bin/museum"
|
|
||||||
|
|
||||||
# Check if museum binary exists
|
|
||||||
if [ ! -f "$MUSEUM_BIN" ]; then
|
|
||||||
echo "Error: Museum binary not found at $MUSEUM_BIN"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Function to update user subscription
|
|
||||||
update_subscription() {
|
|
||||||
local user_email="$1"
|
|
||||||
local storage_gb="$2"
|
|
||||||
local valid_days="$3"
|
|
||||||
|
|
||||||
if [ -z "$user_email" ] || [ -z "$storage_gb" ] || [ -z "$valid_days" ]; then
|
|
||||||
echo "Usage: $0 update-subscription <user-email> <storage-gb> <valid-days>"
|
|
||||||
echo "Example: $0 update-subscription user@example.com 100 365"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Updating subscription for: $user_email"
|
|
||||||
echo "Storage: ${storage_gb}GB"
|
|
||||||
echo "Valid for: ${valid_days} days"
|
|
||||||
|
|
||||||
cd /app/data/museum
|
|
||||||
|
|
||||||
# Use environment variables for database connection
|
|
||||||
export DB_HOST="$CLOUDRON_POSTGRESQL_HOST"
|
|
||||||
export DB_PORT="$CLOUDRON_POSTGRESQL_PORT"
|
|
||||||
export DB_NAME="$CLOUDRON_POSTGRESQL_DATABASE"
|
|
||||||
export DB_USERNAME="$CLOUDRON_POSTGRESQL_USERNAME"
|
|
||||||
export DB_PASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD"
|
|
||||||
|
|
||||||
# Museum admin commands need specific syntax
|
|
||||||
"$MUSEUM_BIN" admin update-subscription "$user_email" "$storage_gb" "$valid_days"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to get user details
|
|
||||||
get_user_details() {
|
|
||||||
local user_email="$1"
|
|
||||||
|
|
||||||
if [ -z "$user_email" ]; then
|
|
||||||
echo "Usage: $0 get-user <user-email>"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd /app/data/museum
|
|
||||||
|
|
||||||
"$MUSEUM_BIN" admin get-user-details --user "$user_email"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to list all users
|
|
||||||
list_users() {
|
|
||||||
cd /app/data/museum
|
|
||||||
|
|
||||||
# Connect to PostgreSQL and list users
|
|
||||||
PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \
|
|
||||||
-h "$CLOUDRON_POSTGRESQL_HOST" \
|
|
||||||
-p "$CLOUDRON_POSTGRESQL_PORT" \
|
|
||||||
-U "$CLOUDRON_POSTGRESQL_USERNAME" \
|
|
||||||
-d "$CLOUDRON_POSTGRESQL_DATABASE" \
|
|
||||||
-c "SELECT email, storage_bonus, subscription_expiry FROM users ORDER BY email;"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main command handler
|
|
||||||
case "$1" in
|
|
||||||
"update-subscription")
|
|
||||||
update_subscription "$2" "$3" "$4"
|
|
||||||
;;
|
|
||||||
"get-user")
|
|
||||||
get_user_details "$2"
|
|
||||||
;;
|
|
||||||
"list-users")
|
|
||||||
list_users
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Ente Admin Helper"
|
|
||||||
echo ""
|
|
||||||
echo "Usage:"
|
|
||||||
echo " $0 update-subscription <user-email> <storage-gb> <valid-days>"
|
|
||||||
echo " $0 get-user <user-email>"
|
|
||||||
echo " $0 list-users"
|
|
||||||
echo ""
|
|
||||||
echo "Examples:"
|
|
||||||
echo " $0 update-subscription user@example.com 100 365"
|
|
||||||
echo " $0 get-user user@example.com"
|
|
||||||
echo " $0 list-users"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
+6
-10
@@ -18,20 +18,16 @@ database:
|
|||||||
maxIdleConns: 25
|
maxIdleConns: 25
|
||||||
connMaxLifetime: "1h"
|
connMaxLifetime: "1h"
|
||||||
|
|
||||||
|
storage:
|
||||||
|
type: "s3"
|
||||||
s3:
|
s3:
|
||||||
are_local_buckets: false
|
|
||||||
use_path_style_urls: true
|
|
||||||
hot_storage:
|
|
||||||
primary: b2-eu-cen
|
|
||||||
secondary: b2-eu-cen
|
|
||||||
derived-storage: b2-eu-cen
|
|
||||||
b2-eu-cen:
|
|
||||||
endpoint: "%%S3_ENDPOINT%%"
|
endpoint: "%%S3_ENDPOINT%%"
|
||||||
region: "%%S3_REGION%%"
|
region: "%%S3_REGION%%"
|
||||||
bucket: "%%S3_BUCKET%%"
|
bucket: "%%S3_BUCKET%%"
|
||||||
key: "%%S3_ACCESS_KEY%%"
|
accessKey: "%%S3_ACCESS_KEY%%"
|
||||||
secret: "%%S3_SECRET_KEY%%"
|
secretKey: "%%S3_SECRET_KEY%%"
|
||||||
path_prefix: "%%S3_PREFIX%%"
|
prefix: "%%S3_PREFIX%%"
|
||||||
|
forcePathStyle: true
|
||||||
|
|
||||||
email:
|
email:
|
||||||
smtp:
|
smtp:
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "==> Debugging Caddy MIME type headers"
|
|
||||||
echo "==> Testing various file types..."
|
|
||||||
|
|
||||||
BASE_URL="${1:-https://ente.due.ren}"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Testing HTML files:"
|
|
||||||
curl -I "$BASE_URL/" 2>/dev/null | grep -i content-type || echo "No Content-Type header found"
|
|
||||||
curl -I "$BASE_URL/index.html" 2>/dev/null | grep -i content-type || echo "No Content-Type header found"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Testing JavaScript files:"
|
|
||||||
curl -I "$BASE_URL/config.js" 2>/dev/null | grep -i content-type || echo "No Content-Type header found"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Testing CSS files (if any):"
|
|
||||||
curl -I "$BASE_URL/styles.css" 2>/dev/null | grep -i content-type || echo "File not found or no Content-Type header"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Testing JSON files (if any):"
|
|
||||||
curl -I "$BASE_URL/manifest.json" 2>/dev/null | grep -i content-type || echo "File not found or no Content-Type header"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "==> Full response headers for main page:"
|
|
||||||
curl -I "$BASE_URL/" 2>/dev/null || echo "Failed to connect to $BASE_URL"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "==> To test from inside a container:"
|
|
||||||
echo "docker exec -it <container-name> curl -I http://localhost:3080/"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "==> To view Caddy logs:"
|
|
||||||
echo "docker exec -it <container-name> tail -f /app/data/logs/caddy.log"
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Debug Ente Auth Network Calls</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Debug Ente Auth Network Calls</h1>
|
|
||||||
<div id="output"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Override fetch to log all network requests
|
|
||||||
const originalFetch = window.fetch;
|
|
||||||
window.fetch = function(...args) {
|
|
||||||
console.log('FETCH REQUEST:', args[0], args[1]);
|
|
||||||
const output = document.getElementById('output');
|
|
||||||
output.innerHTML += '<p>FETCH: ' + args[0] + '</p>';
|
|
||||||
return originalFetch.apply(this, args)
|
|
||||||
.then(response => {
|
|
||||||
console.log('FETCH RESPONSE:', response.status, response.url);
|
|
||||||
output.innerHTML += '<p>RESPONSE: ' + response.status + ' ' + response.url + '</p>';
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log('FETCH ERROR:', error);
|
|
||||||
output.innerHTML += '<p>ERROR: ' + error.message + '</p>';
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the Ente Auth app in an iframe to see what happens
|
|
||||||
const iframe = document.createElement('iframe');
|
|
||||||
iframe.src = 'https://ente.due.ren/auth/';
|
|
||||||
iframe.style.width = '100%';
|
|
||||||
iframe.style.height = '400px';
|
|
||||||
document.body.appendChild(iframe);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Add this debugging section to your start.sh after line 350
|
|
||||||
|
|
||||||
# Start Caddy with more verbose logging
|
|
||||||
echo "==> Starting Caddy web server with debug logging"
|
|
||||||
echo "==> Validating Caddyfile first..."
|
|
||||||
caddy validate --config /app/data/Caddyfile --adapter caddyfile || {
|
|
||||||
echo "==> ERROR: Caddyfile validation failed!"
|
|
||||||
cat /app/data/Caddyfile
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "==> Starting Caddy..."
|
|
||||||
# Run Caddy in foreground first to see errors
|
|
||||||
timeout 10 caddy run --config /app/data/Caddyfile --adapter caddyfile 2>&1 | tee /app/data/logs/caddy-debug.log || {
|
|
||||||
echo "==> ERROR: Caddy failed to start"
|
|
||||||
echo "==> Last 50 lines of Caddy debug log:"
|
|
||||||
tail -50 /app/data/logs/caddy-debug.log
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if port is actually listening
|
|
||||||
echo "==> Checking if port 3080 is listening..."
|
|
||||||
netstat -tlnp | grep 3080 || lsof -i :3080 || {
|
|
||||||
echo "==> ERROR: Nothing listening on port 3080"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test the health endpoint
|
|
||||||
echo "==> Testing health endpoint..."
|
|
||||||
curl -v http://localhost:3080/health || {
|
|
||||||
echo "==> ERROR: Health check failed"
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# Ente CLI Configuration for Custom Server
|
|
||||||
|
|
||||||
The Ente CLI expects configuration in `~/.ente/config.yaml`. Here's how to set it up:
|
|
||||||
|
|
||||||
## Method 1: Direct Configuration
|
|
||||||
|
|
||||||
1. Create the config file:
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/.ente
|
|
||||||
cat > ~/.ente/config.yaml << EOF
|
|
||||||
api:
|
|
||||||
url: https://ente.due.ren
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add your account interactively:
|
|
||||||
```bash
|
|
||||||
ente account add
|
|
||||||
# It will ask for:
|
|
||||||
# - Export directory: /tmp/ente-export (or any directory)
|
|
||||||
# - Email: your-admin@email.com
|
|
||||||
# - Password: your-password
|
|
||||||
```
|
|
||||||
|
|
||||||
## Method 2: Using the Admin Commands Directly
|
|
||||||
|
|
||||||
If the interactive setup is problematic, you can use the admin commands with explicit parameters:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Set the API endpoint
|
|
||||||
export ENTE_API_URL="https://ente.due.ren"
|
|
||||||
|
|
||||||
# Or pass it directly in the command
|
|
||||||
ente admin update-subscription \
|
|
||||||
--api-url https://ente.due.ren \
|
|
||||||
--admin-user admin@due.ren \
|
|
||||||
--user user@example.com \
|
|
||||||
--storage 1000 \
|
|
||||||
--valid-for 365
|
|
||||||
```
|
|
||||||
|
|
||||||
## Method 3: Direct Database Update (Fallback)
|
|
||||||
|
|
||||||
Since the CLI setup seems problematic, you can update the database directly in the Cloudron terminal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In Cloudron terminal
|
|
||||||
PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \
|
|
||||||
-h "$CLOUDRON_POSTGRESQL_HOST" \
|
|
||||||
-U "$CLOUDRON_POSTGRESQL_USERNAME" \
|
|
||||||
-d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF
|
|
||||||
-- Update user to 1TB for 1 year
|
|
||||||
UPDATE users
|
|
||||||
SET storage_bonus = 1073741824000, -- 1000 GB in bytes
|
|
||||||
subscription_expiry = EXTRACT(EPOCH FROM NOW() + INTERVAL '365 days') * 1000000
|
|
||||||
WHERE email = 'andreas@due.ren';
|
|
||||||
|
|
||||||
-- Show the result
|
|
||||||
SELECT email,
|
|
||||||
storage_bonus / 1073741824.0 as storage_gb,
|
|
||||||
to_timestamp(subscription_expiry / 1000000) as expires
|
|
||||||
FROM users WHERE email = 'andreas@due.ren';
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Setup Ente CLI for custom server
|
|
||||||
|
|
||||||
echo "Setting up Ente CLI for custom server..."
|
|
||||||
|
|
||||||
# Create config directory
|
|
||||||
mkdir -p ~/.ente
|
|
||||||
|
|
||||||
# Create the CLI config with custom endpoint
|
|
||||||
cat > ~/.ente/config.yaml << EOF
|
|
||||||
host: https://ente.due.ren
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "Configuration created at ~/.ente/config.yaml"
|
|
||||||
echo ""
|
|
||||||
echo "Now you can add your account:"
|
|
||||||
echo " ente account add"
|
|
||||||
echo ""
|
|
||||||
echo "Then use admin commands:"
|
|
||||||
echo " ente admin update-subscription --admin-user admin@due.ren --user user@example.com --storage 1000 --valid-for 365"
|
|
||||||
-150
@@ -1,150 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Better signal handling - forward signals to child processes
|
|
||||||
trap 'kill -TERM $SERVER_PID; kill -TERM $CADDY_PID; exit' TERM INT
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
echo "==> Starting Ente Cloudron app (DEBUG MODE)..."
|
|
||||||
|
|
||||||
# Create necessary directories
|
|
||||||
mkdir -p /app/data/config /app/data/logs /app/data/caddy
|
|
||||||
|
|
||||||
# Check if web directories exist
|
|
||||||
echo "==> Checking web app directories:"
|
|
||||||
for app in photos accounts auth cast; do
|
|
||||||
if [ -d "/app/web/$app" ]; then
|
|
||||||
echo "==> Found: /app/web/$app"
|
|
||||||
ls -la "/app/web/$app" | head -5
|
|
||||||
else
|
|
||||||
echo "==> WARNING: Missing /app/web/$app - creating placeholder"
|
|
||||||
mkdir -p "/app/web/$app"
|
|
||||||
echo "<html><body><h1>$app app placeholder</h1></body></html>" > "/app/web/$app/index.html"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Create a simple test Caddyfile first
|
|
||||||
echo "==> Creating simple test Caddyfile"
|
|
||||||
cat > /app/data/Caddyfile <<'EOT'
|
|
||||||
{
|
|
||||||
admin off
|
|
||||||
auto_https off
|
|
||||||
}
|
|
||||||
|
|
||||||
:3080 {
|
|
||||||
log {
|
|
||||||
output stdout
|
|
||||||
format console
|
|
||||||
level DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
# Health check endpoint
|
|
||||||
handle /health {
|
|
||||||
header Content-Type "application/json"
|
|
||||||
respond "{\"status\": \"OK\", \"timestamp\": \"{{now | date \"2006-01-02T15:04:05Z07:00\"}}\"}" 200
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test endpoint
|
|
||||||
handle /test {
|
|
||||||
respond "Caddy is working on port 3080!" 200
|
|
||||||
}
|
|
||||||
|
|
||||||
# API proxy to Museum server
|
|
||||||
handle /api/* {
|
|
||||||
uri strip_prefix /api
|
|
||||||
reverse_proxy localhost:8080 {
|
|
||||||
transport http {
|
|
||||||
read_timeout 60s
|
|
||||||
write_timeout 60s
|
|
||||||
}
|
|
||||||
# Add error handling
|
|
||||||
handle_errors {
|
|
||||||
respond "{\"error\": \"Museum server not available\"}" 503
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Serve web apps with fallback
|
|
||||||
handle {
|
|
||||||
root * /app/web/photos
|
|
||||||
try_files {path} {path}/ /index.html
|
|
||||||
file_server {
|
|
||||||
browse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOT
|
|
||||||
|
|
||||||
# Start a simple Museum mock server for testing
|
|
||||||
echo "==> Starting mock Museum server on port 8080"
|
|
||||||
cat > /tmp/museum-mock.js <<'EOF'
|
|
||||||
const http = require('http');
|
|
||||||
const server = http.createServer((req, res) => {
|
|
||||||
console.log(`Museum mock: ${req.method} ${req.url}`);
|
|
||||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
||||||
res.end(JSON.stringify({ status: 'ok', path: req.url, timestamp: new Date().toISOString() }));
|
|
||||||
});
|
|
||||||
server.listen(8080, '127.0.0.1', () => {
|
|
||||||
console.log('Museum mock server running on http://127.0.0.1:8080');
|
|
||||||
});
|
|
||||||
EOF
|
|
||||||
node /tmp/museum-mock.js > /app/data/logs/museum-mock.log 2>&1 &
|
|
||||||
SERVER_PID=$!
|
|
||||||
echo "==> Mock Museum server started (PID: $SERVER_PID)"
|
|
||||||
|
|
||||||
# Wait for Museum mock to be ready
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Test Museum mock
|
|
||||||
echo "==> Testing Museum mock server..."
|
|
||||||
curl -s http://localhost:8080/test || echo "WARNING: Museum mock not responding"
|
|
||||||
|
|
||||||
# Validate Caddyfile
|
|
||||||
echo "==> Validating Caddyfile..."
|
|
||||||
caddy validate --config /app/data/Caddyfile --adapter caddyfile || {
|
|
||||||
echo "==> ERROR: Caddyfile validation failed!"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Start Caddy with explicit environment
|
|
||||||
echo "==> Starting Caddy web server..."
|
|
||||||
CADDY_FORMAT=console caddy run --config /app/data/Caddyfile --adapter caddyfile 2>&1 | tee /app/data/logs/caddy-combined.log &
|
|
||||||
CADDY_PID=$!
|
|
||||||
echo "==> Caddy started (PID: $CADDY_PID)"
|
|
||||||
|
|
||||||
# Wait for Caddy to start
|
|
||||||
echo "==> Waiting for Caddy to start..."
|
|
||||||
for i in {1..30}; do
|
|
||||||
if curl -s http://localhost:3080/health > /dev/null; then
|
|
||||||
echo "==> Caddy is responding!"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo -n "."
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Check process status
|
|
||||||
echo "==> Process status:"
|
|
||||||
ps aux | grep -E "(caddy|node)" | grep -v grep || echo "No processes found"
|
|
||||||
|
|
||||||
# Check port status
|
|
||||||
echo "==> Port status:"
|
|
||||||
netstat -tlnp 2>/dev/null | grep -E "(3080|8080)" || lsof -i :3080 -i :8080 2>/dev/null || echo "Cannot check port status"
|
|
||||||
|
|
||||||
# Test endpoints
|
|
||||||
echo "==> Testing endpoints:"
|
|
||||||
echo "Health check:"
|
|
||||||
curl -s http://localhost:3080/health | jq . || echo "Failed"
|
|
||||||
echo -e "\nTest endpoint:"
|
|
||||||
curl -s http://localhost:3080/test || echo "Failed"
|
|
||||||
echo -e "\nAPI proxy:"
|
|
||||||
curl -s http://localhost:3080/api/status | jq . || echo "Failed"
|
|
||||||
|
|
||||||
echo "==> Startup complete. Services:"
|
|
||||||
echo " - Caddy PID: $CADDY_PID"
|
|
||||||
echo " - Museum Mock PID: $SERVER_PID"
|
|
||||||
echo "==> Logs: /app/data/logs/"
|
|
||||||
|
|
||||||
# Keep running
|
|
||||||
wait $SERVER_PID $CADDY_PID
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Script to update Ente user storage using the Ente CLI
|
|
||||||
# Run this from your local machine (not inside Cloudron)
|
|
||||||
|
|
||||||
# Check if ente CLI is installed
|
|
||||||
if ! command -v ente &> /dev/null; then
|
|
||||||
echo "Ente CLI is not installed. Please install it first:"
|
|
||||||
echo ""
|
|
||||||
echo "For macOS:"
|
|
||||||
echo " brew tap ente-io/ente"
|
|
||||||
echo " brew install ente-cli"
|
|
||||||
echo ""
|
|
||||||
echo "For other systems, download from:"
|
|
||||||
echo " https://github.com/ente-io/ente/releases"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Your Ente instance
|
|
||||||
ENTE_ENDPOINT="https://ente.due.ren"
|
|
||||||
|
|
||||||
# Function to update subscription
|
|
||||||
update_subscription() {
|
|
||||||
local admin_email="$1"
|
|
||||||
local user_email="$2"
|
|
||||||
local storage_gb="$3"
|
|
||||||
local valid_days="$4"
|
|
||||||
|
|
||||||
echo "Updating subscription for: $user_email"
|
|
||||||
echo "Storage: ${storage_gb}GB"
|
|
||||||
echo "Valid for: ${valid_days} days"
|
|
||||||
echo "Using admin account: $admin_email"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Run the ente CLI command
|
|
||||||
ente admin update-subscription \
|
|
||||||
--host "$ENTE_ENDPOINT" \
|
|
||||||
--admin-user "$admin_email" \
|
|
||||||
--user "$user_email" \
|
|
||||||
--storage "$storage_gb" \
|
|
||||||
--valid-for "$valid_days"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check arguments
|
|
||||||
if [ $# -lt 4 ]; then
|
|
||||||
echo "Usage: $0 <admin-email> <user-email> <storage-gb> <valid-days>"
|
|
||||||
echo ""
|
|
||||||
echo "Example:"
|
|
||||||
echo " $0 admin@due.ren andreas@due.ren 1000 365"
|
|
||||||
echo ""
|
|
||||||
echo "Make sure you're logged in to the Ente CLI first:"
|
|
||||||
echo " ente account add"
|
|
||||||
echo " API endpoint: $ENTE_ENDPOINT"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run the update
|
|
||||||
update_subscription "$1" "$2" "$3" "$4"
|
|
||||||
Reference in New Issue
Block a user