Compare commits
10 Commits
4e091002a7
...
17bb921941
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17bb921941 | ||
|
|
21a9ac6c86 | ||
|
|
1f3505f132 | ||
|
|
b8d38f52a2 | ||
|
|
294a84a414 | ||
|
|
0bda151abe | ||
|
|
0877db4c36 | ||
|
|
aee56753d5 | ||
|
|
e758749bd6 | ||
|
|
7a253e0f0c |
@@ -5,8 +5,9 @@
|
|||||||
"description": "Next-gen knowledge base that blends docs, whiteboards, and databases for self-hosted teams.",
|
"description": "Next-gen knowledge base that blends docs, whiteboards, and databases for self-hosted teams.",
|
||||||
"website": "https://affine.pro",
|
"website": "https://affine.pro",
|
||||||
"contactEmail": "support@affine.pro",
|
"contactEmail": "support@affine.pro",
|
||||||
"version": "0.1.6",
|
"version": "0.25.3",
|
||||||
"changelog": "Initial Cloudron packaging",
|
"changelog": "Upgrade upstream AFFiNE runtime to v0.25.3 and keep Cloudron OIDC wiring",
|
||||||
|
"icon": "file://icon.png",
|
||||||
"manifestVersion": 2,
|
"manifestVersion": 2,
|
||||||
"minBoxVersion": "7.0.0",
|
"minBoxVersion": "7.0.0",
|
||||||
"httpPort": 3000,
|
"httpPort": 3000,
|
||||||
@@ -16,10 +17,9 @@
|
|||||||
"redis": {},
|
"redis": {},
|
||||||
"sendmail": {},
|
"sendmail": {},
|
||||||
"oidc": {
|
"oidc": {
|
||||||
"redirectUris": [
|
"loginRedirectUri": "/oauth/callback",
|
||||||
"/api/v1/session/callback"
|
"logoutRedirectUri": "/",
|
||||||
],
|
"tokenSignatureAlgorithm": "RS256"
|
||||||
"loginRedirectUri": "/api/v1/session/callback"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memoryLimit": 2147483648,
|
"memoryLimit": 2147483648,
|
||||||
|
|||||||
207
start.sh
207
start.sh
@@ -15,6 +15,14 @@ log() {
|
|||||||
printf '[%s] %s\n' "$(date --iso-8601=seconds)" "$*"
|
printf '[%s] %s\n' "$(date --iso-8601=seconds)" "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record_env_var() {
|
||||||
|
local name="$1"
|
||||||
|
local value="$2"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
printf '%s=%q\n' "$name" "$value" >> "$ENV_EXPORT_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
require_env() {
|
require_env() {
|
||||||
local var_name="$1"
|
local var_name="$1"
|
||||||
if [ -z "${!var_name:-}" ]; then
|
if [ -z "${!var_name:-}" ]; then
|
||||||
@@ -26,7 +34,8 @@ require_env() {
|
|||||||
prepare_data_dirs() {
|
prepare_data_dirs() {
|
||||||
log "Preparing persistent directories"
|
log "Preparing persistent directories"
|
||||||
mkdir -p "$APP_DATA_DIR/config" "$APP_DATA_DIR/storage" "$APP_DATA_DIR/logs" "$APP_RUNTIME_DIR" "$APP_HOME_DIR" "$AFFINE_HOME"
|
mkdir -p "$APP_DATA_DIR/config" "$APP_DATA_DIR/storage" "$APP_DATA_DIR/logs" "$APP_RUNTIME_DIR" "$APP_HOME_DIR" "$AFFINE_HOME"
|
||||||
mkdir -p /run/nginx/body /run/nginx/proxy /run/nginx/fastcgi
|
mkdir -p /run/nginx/body /run/nginx/proxy /run/nginx/fastcgi /run/nginx/uwsgi /run/nginx/scgi
|
||||||
|
: > "$ENV_EXPORT_FILE"
|
||||||
|
|
||||||
if [ ! -f "$APP_DATA_DIR/config/config.json" ]; then
|
if [ ! -f "$APP_DATA_DIR/config/config.json" ]; then
|
||||||
log "Seeding default configuration"
|
log "Seeding default configuration"
|
||||||
@@ -48,6 +57,19 @@ prepare_data_dirs() {
|
|||||||
chown -R cloudron:cloudron "$APP_DATA_DIR" "$APP_RUNTIME_DIR" "$APP_HOME_DIR"
|
chown -R cloudron:cloudron "$APP_DATA_DIR" "$APP_RUNTIME_DIR" "$APP_HOME_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepare_runtime_build_dir() {
|
||||||
|
local source_dir="$APP_BUILD_DIR"
|
||||||
|
local runtime_build_dir="$APP_RUNTIME_DIR/affine-build"
|
||||||
|
log "Syncing AFFiNE runtime into $runtime_build_dir"
|
||||||
|
rm -rf "$runtime_build_dir"
|
||||||
|
mkdir -p "$runtime_build_dir"
|
||||||
|
cp -a "$source_dir/." "$runtime_build_dir/"
|
||||||
|
chown -R cloudron:cloudron "$runtime_build_dir"
|
||||||
|
APP_BUILD_DIR="$runtime_build_dir"
|
||||||
|
export APP_BUILD_DIR
|
||||||
|
record_env_var APP_BUILD_DIR "$APP_BUILD_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
configure_database() {
|
configure_database() {
|
||||||
require_env CLOUDRON_POSTGRESQL_URL
|
require_env CLOUDRON_POSTGRESQL_URL
|
||||||
local db_url="$CLOUDRON_POSTGRESQL_URL"
|
local db_url="$CLOUDRON_POSTGRESQL_URL"
|
||||||
@@ -55,6 +77,7 @@ configure_database() {
|
|||||||
db_url="postgresql://${db_url#postgres://}"
|
db_url="postgresql://${db_url#postgres://}"
|
||||||
fi
|
fi
|
||||||
export DATABASE_URL="$db_url"
|
export DATABASE_URL="$db_url"
|
||||||
|
record_env_var DATABASE_URL "$db_url"
|
||||||
log "Configured PostgreSQL endpoint"
|
log "Configured PostgreSQL endpoint"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,18 +88,44 @@ configure_redis() {
|
|||||||
import os
|
import os
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
url = os.environ.get('CLOUDRON_REDIS_URL')
|
url = os.environ.get('CLOUDRON_REDIS_URL')
|
||||||
if not url:
|
parsed = urlparse(url) if url else None
|
||||||
raise SystemExit('redis url missing')
|
host = os.environ.get('CLOUDRON_REDIS_HOST')
|
||||||
parsed = urlparse(url)
|
port = os.environ.get('CLOUDRON_REDIS_PORT')
|
||||||
host = parsed.hostname or 'localhost'
|
password = os.environ.get('CLOUDRON_REDIS_PASSWORD')
|
||||||
port = parsed.port or 6379
|
username = os.environ.get('CLOUDRON_REDIS_USERNAME')
|
||||||
password = parsed.password or ''
|
db = os.environ.get('CLOUDRON_REDIS_DB')
|
||||||
db = (parsed.path or '/0').lstrip('/') or '0'
|
if not host and parsed:
|
||||||
username = parsed.username or ''
|
host = parsed.hostname or 'localhost'
|
||||||
|
if not port and parsed:
|
||||||
|
port = parsed.port or 6379
|
||||||
|
if not password and parsed:
|
||||||
|
password = parsed.password or ''
|
||||||
|
if not db and parsed:
|
||||||
|
db = (parsed.path or '/0').lstrip('/') or '0'
|
||||||
|
if username is None:
|
||||||
|
username = parsed.username if parsed and parsed.username else 'default'
|
||||||
|
host = host or 'localhost'
|
||||||
|
port = port or 6379
|
||||||
|
password = password or ''
|
||||||
|
db = db or '0'
|
||||||
print(f"{host}\n{port}\n{password}\n{db}\n{username}")
|
print(f"{host}\n{port}\n{password}\n{db}\n{username}")
|
||||||
PY
|
PY
|
||||||
)
|
)
|
||||||
IFS=$'\n' read -r host port password db username <<<"$redis_info"
|
IFS=$'\n' read -r host port password db username <<<"$redis_info"
|
||||||
|
if [ -n "${CLOUDRON_REDIS_HOST:-}" ]; then
|
||||||
|
host="$CLOUDRON_REDIS_HOST"
|
||||||
|
fi
|
||||||
|
if [ -n "${CLOUDRON_REDIS_PORT:-}" ]; then
|
||||||
|
port="$CLOUDRON_REDIS_PORT"
|
||||||
|
fi
|
||||||
|
if [ -n "${CLOUDRON_REDIS_PASSWORD:-}" ]; then
|
||||||
|
password="$CLOUDRON_REDIS_PASSWORD"
|
||||||
|
fi
|
||||||
|
if [ -n "${CLOUDRON_REDIS_USERNAME:-}" ]; then
|
||||||
|
username="$CLOUDRON_REDIS_USERNAME"
|
||||||
|
elif [ -z "$username" ]; then
|
||||||
|
username="default"
|
||||||
|
fi
|
||||||
export REDIS_SERVER_HOST="$host"
|
export REDIS_SERVER_HOST="$host"
|
||||||
export REDIS_SERVER_PORT="$port"
|
export REDIS_SERVER_PORT="$port"
|
||||||
export REDIS_SERVER_PASSWORD="$password"
|
export REDIS_SERVER_PASSWORD="$password"
|
||||||
@@ -84,20 +133,79 @@ PY
|
|||||||
export REDIS_SERVER_USERNAME="$username"
|
export REDIS_SERVER_USERNAME="$username"
|
||||||
export REDIS_URL="$CLOUDRON_REDIS_URL"
|
export REDIS_URL="$CLOUDRON_REDIS_URL"
|
||||||
export REDIS_SERVER_URL="$CLOUDRON_REDIS_URL"
|
export REDIS_SERVER_URL="$CLOUDRON_REDIS_URL"
|
||||||
|
record_env_var REDIS_SERVER_HOST "$host"
|
||||||
|
record_env_var REDIS_SERVER_PORT "$port"
|
||||||
|
record_env_var REDIS_SERVER_PASSWORD "$password"
|
||||||
|
record_env_var REDIS_SERVER_DATABASE "$db"
|
||||||
|
record_env_var REDIS_SERVER_USERNAME "$username"
|
||||||
|
record_env_var REDIS_URL "$CLOUDRON_REDIS_URL"
|
||||||
|
record_env_var REDIS_SERVER_URL "$CLOUDRON_REDIS_URL"
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
config_path = Path(os.environ['APP_DATA_DIR']) / 'config' / 'config.json'
|
||||||
|
data = json.loads(config_path.read_text())
|
||||||
|
redis = data.setdefault('redis', {})
|
||||||
|
redis['host'] = os.environ.get('REDIS_SERVER_HOST', '')
|
||||||
|
redis['port'] = int(os.environ.get('REDIS_SERVER_PORT') or 6379)
|
||||||
|
redis['password'] = os.environ.get('REDIS_SERVER_PASSWORD', '')
|
||||||
|
redis['username'] = os.environ.get('REDIS_SERVER_USERNAME', '')
|
||||||
|
redis['db'] = int(os.environ.get('REDIS_SERVER_DATABASE') or 0)
|
||||||
|
config_path.write_text(json.dumps(data, indent=2))
|
||||||
|
PY
|
||||||
log "Configured Redis endpoint"
|
log "Configured Redis endpoint"
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_mail() {
|
configure_mail() {
|
||||||
if [ -z "${CLOUDRON_MAIL_SMTP_SERVER:-}" ]; then
|
local host=""
|
||||||
log "Cloudron mail addon not configured, skipping SMTP setup"
|
local port=""
|
||||||
|
local user=""
|
||||||
|
local password=""
|
||||||
|
local sender=""
|
||||||
|
local ignore_tls="false"
|
||||||
|
|
||||||
|
if [ -n "${CLOUDRON_EMAIL_SMTP_SERVER:-}" ]; then
|
||||||
|
host="$CLOUDRON_EMAIL_SMTP_SERVER"
|
||||||
|
port="${CLOUDRON_EMAIL_SMTPS_PORT:-${CLOUDRON_EMAIL_SMTP_PORT:-587}}"
|
||||||
|
user="${CLOUDRON_EMAIL_SMTP_USERNAME:-}"
|
||||||
|
password="${CLOUDRON_EMAIL_SMTP_PASSWORD:-}"
|
||||||
|
sender="${CLOUDRON_EMAIL_FROM:-AFFiNE <no-reply@cloudron.local>}"
|
||||||
|
ignore_tls="${MAILER_IGNORE_TLS:-true}"
|
||||||
|
log "Configuring SMTP using Cloudron email addon"
|
||||||
|
elif [ -n "${CLOUDRON_MAIL_SMTP_SERVER:-}" ]; then
|
||||||
|
host="$CLOUDRON_MAIL_SMTP_SERVER"
|
||||||
|
port="${CLOUDRON_MAIL_SMTP_PORT:-587}"
|
||||||
|
user="${CLOUDRON_MAIL_SMTP_USERNAME:-}"
|
||||||
|
password="${CLOUDRON_MAIL_SMTP_PASSWORD:-}"
|
||||||
|
sender="${CLOUDRON_MAIL_FROM:-AFFiNE <no-reply@cloudron.local>}"
|
||||||
|
ignore_tls="${MAILER_IGNORE_TLS:-false}"
|
||||||
|
if [ -n "${CLOUDRON_MAIL_SMTP_SECURE:-}" ]; then
|
||||||
|
case "${CLOUDRON_MAIL_SMTP_SECURE,,}" in
|
||||||
|
true|1|yes) port="${CLOUDRON_MAIL_SMTP_PORT:-465}" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
log "Configuring SMTP using Cloudron sendmail addon"
|
||||||
|
else
|
||||||
|
log "Cloudron mail/email addon not configured, skipping SMTP setup"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
export MAILER_HOST="$CLOUDRON_MAIL_SMTP_SERVER"
|
|
||||||
export MAILER_PORT="${CLOUDRON_MAIL_SMTP_PORT:-587}"
|
export MAILER_HOST="$host"
|
||||||
export MAILER_USER="${CLOUDRON_MAIL_SMTP_USERNAME:-}"
|
export MAILER_PORT="$port"
|
||||||
export MAILER_PASSWORD="${CLOUDRON_MAIL_SMTP_PASSWORD:-}"
|
export MAILER_USER="$user"
|
||||||
export MAILER_SENDER="${CLOUDRON_MAIL_FROM:-AFFiNE <no-reply@cloudron.local>}"
|
export MAILER_PASSWORD="$password"
|
||||||
|
export MAILER_SENDER="${sender:-AFFiNE <no-reply@cloudron.local>}"
|
||||||
export MAILER_SERVERNAME="${MAILER_SERVERNAME:-AFFiNE Server}"
|
export MAILER_SERVERNAME="${MAILER_SERVERNAME:-AFFiNE Server}"
|
||||||
|
export MAILER_IGNORE_TLS="$ignore_tls"
|
||||||
|
|
||||||
|
record_env_var MAILER_HOST "$MAILER_HOST"
|
||||||
|
record_env_var MAILER_PORT "$MAILER_PORT"
|
||||||
|
record_env_var MAILER_USER "$MAILER_USER"
|
||||||
|
record_env_var MAILER_PASSWORD "$MAILER_PASSWORD"
|
||||||
|
record_env_var MAILER_SENDER "$MAILER_SENDER"
|
||||||
|
record_env_var MAILER_SERVERNAME "$MAILER_SERVERNAME"
|
||||||
|
record_env_var MAILER_IGNORE_TLS "$MAILER_IGNORE_TLS"
|
||||||
log "Configured SMTP relay"
|
log "Configured SMTP relay"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,37 +229,10 @@ PY
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
export AFFINE_INDEXER_ENABLED=${AFFINE_INDEXER_ENABLED:-false}
|
export AFFINE_INDEXER_ENABLED=${AFFINE_INDEXER_ENABLED:-false}
|
||||||
}
|
record_env_var AFFINE_SERVER_EXTERNAL_URL "${AFFINE_SERVER_EXTERNAL_URL:-}"
|
||||||
|
record_env_var AFFINE_SERVER_HOST "${AFFINE_SERVER_HOST:-}"
|
||||||
write_runtime_env() {
|
record_env_var AFFINE_SERVER_HTTPS "${AFFINE_SERVER_HTTPS:-}"
|
||||||
: > "$ENV_EXPORT_FILE"
|
record_env_var AFFINE_INDEXER_ENABLED "$AFFINE_INDEXER_ENABLED"
|
||||||
local vars=(
|
|
||||||
DATABASE_URL
|
|
||||||
REDIS_SERVER_HOST
|
|
||||||
REDIS_SERVER_PORT
|
|
||||||
REDIS_SERVER_PASSWORD
|
|
||||||
REDIS_SERVER_DATABASE
|
|
||||||
REDIS_SERVER_USERNAME
|
|
||||||
REDIS_URL
|
|
||||||
REDIS_SERVER_URL
|
|
||||||
MAILER_HOST
|
|
||||||
MAILER_PORT
|
|
||||||
MAILER_USER
|
|
||||||
MAILER_PASSWORD
|
|
||||||
MAILER_SENDER
|
|
||||||
MAILER_SERVERNAME
|
|
||||||
AFFINE_SERVER_EXTERNAL_URL
|
|
||||||
AFFINE_SERVER_HOST
|
|
||||||
AFFINE_SERVER_HTTPS
|
|
||||||
AFFINE_INDEXER_ENABLED
|
|
||||||
)
|
|
||||||
local var value
|
|
||||||
for var in "${vars[@]}"; do
|
|
||||||
value="${!var-}"
|
|
||||||
if [ -n "$value" ]; then
|
|
||||||
printf '%s=%q\n' "$var" "$value" >> "$ENV_EXPORT_FILE"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_auth() {
|
configure_auth() {
|
||||||
@@ -160,6 +241,7 @@ configure_auth() {
|
|||||||
python3 - <<'PY'
|
python3 - <<'PY'
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
config_path = Path(os.environ['APP_DATA_DIR']) / 'config' / 'config.json'
|
config_path = Path(os.environ['APP_DATA_DIR']) / 'config' / 'config.json'
|
||||||
data = json.loads(config_path.read_text())
|
data = json.loads(config_path.read_text())
|
||||||
@@ -168,9 +250,34 @@ providers = auth.setdefault('providers', {})
|
|||||||
oidc = providers.setdefault('oidc', {})
|
oidc = providers.setdefault('oidc', {})
|
||||||
oidc['clientId'] = os.environ.get('CLOUDRON_OIDC_CLIENT_ID', '')
|
oidc['clientId'] = os.environ.get('CLOUDRON_OIDC_CLIENT_ID', '')
|
||||||
oidc['clientSecret'] = os.environ.get('CLOUDRON_OIDC_CLIENT_SECRET', '')
|
oidc['clientSecret'] = os.environ.get('CLOUDRON_OIDC_CLIENT_SECRET', '')
|
||||||
oidc['issuer'] = os.environ.get('CLOUDRON_OIDC_ISSUER') or os.environ.get('CLOUDRON_OIDC_DISCOVERY_URL', '')
|
issuer = os.environ.get('CLOUDRON_OIDC_ISSUER') or ''
|
||||||
|
discovery = os.environ.get('CLOUDRON_OIDC_DISCOVERY_URL') or ''
|
||||||
|
resolved_issuer = issuer
|
||||||
|
if not resolved_issuer and discovery:
|
||||||
|
resolved_issuer = re.sub(r'/\.well-known.*$', '', discovery)
|
||||||
|
if not resolved_issuer:
|
||||||
|
resolved_issuer = discovery
|
||||||
|
oidc['issuer'] = resolved_issuer
|
||||||
|
default_scope = os.environ.get('AFFINE_OIDC_SCOPE', 'openid profile email')
|
||||||
|
default_claims = {
|
||||||
|
'claim_id': os.environ.get('AFFINE_OIDC_CLAIM_ID', 'preferred_username'),
|
||||||
|
'claim_email': os.environ.get('AFFINE_OIDC_CLAIM_EMAIL', 'email'),
|
||||||
|
'claim_name': os.environ.get('AFFINE_OIDC_CLAIM_NAME', 'name'),
|
||||||
|
}
|
||||||
args = oidc.setdefault('args', {})
|
args = oidc.setdefault('args', {})
|
||||||
args.setdefault('scope', 'openid profile email')
|
args['scope'] = default_scope
|
||||||
|
for key, value in default_claims.items():
|
||||||
|
args.setdefault(key, value)
|
||||||
|
oauth = data.setdefault('oauth', {})
|
||||||
|
oauth_providers = oauth.setdefault('providers', {})
|
||||||
|
oauth_oidc = oauth_providers.setdefault('oidc', {})
|
||||||
|
oauth_oidc['clientId'] = oidc['clientId']
|
||||||
|
oauth_oidc['clientSecret'] = oidc['clientSecret']
|
||||||
|
oauth_oidc['issuer'] = resolved_issuer
|
||||||
|
oauth_args = oauth_oidc.setdefault('args', {})
|
||||||
|
oauth_args['scope'] = default_scope
|
||||||
|
for key, value in default_claims.items():
|
||||||
|
oauth_args.setdefault(key, value)
|
||||||
config_path.write_text(json.dumps(data, indent=2))
|
config_path.write_text(json.dumps(data, indent=2))
|
||||||
PY
|
PY
|
||||||
log "Enabled Cloudron OIDC for AFFiNE"
|
log "Enabled Cloudron OIDC for AFFiNE"
|
||||||
@@ -199,13 +306,13 @@ PY
|
|||||||
main() {
|
main() {
|
||||||
export HOME="$APP_HOME_DIR"
|
export HOME="$APP_HOME_DIR"
|
||||||
prepare_data_dirs
|
prepare_data_dirs
|
||||||
|
prepare_runtime_build_dir
|
||||||
configure_database
|
configure_database
|
||||||
configure_redis
|
configure_redis
|
||||||
configure_mail
|
configure_mail
|
||||||
configure_server_metadata
|
configure_server_metadata
|
||||||
update_server_config
|
update_server_config
|
||||||
configure_auth
|
configure_auth
|
||||||
write_runtime_env
|
|
||||||
chown -R cloudron:cloudron "$APP_DATA_DIR" "$APP_HOME_DIR"
|
chown -R cloudron:cloudron "$APP_DATA_DIR" "$APP_HOME_DIR"
|
||||||
log "Starting supervisor"
|
log "Starting supervisor"
|
||||||
exec /usr/bin/supervisord -c "$APP_CODE_DIR/supervisord.conf"
|
exec /usr/bin/supervisord -c "$APP_CODE_DIR/supervisord.conf"
|
||||||
|
|||||||
Reference in New Issue
Block a user