Prepare clean production setup and coming-soon cover
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"""Run inside the configured source container. Export configuration, NEVER data.
|
||||
|
||||
Usage: python prepare_production_settings.py /secure/new-directory
|
||||
Creates new files exclusively with mode 0600. No secrets go to stdout.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import sys
|
||||
|
||||
from app.runtime import get_runtime_settings
|
||||
|
||||
|
||||
def prepare(destination: Path) -> None:
|
||||
runtime = get_runtime_settings()
|
||||
keys = [
|
||||
'jellyfin_base_url', 'jellyfin_api_key', 'jellyfin_public_url',
|
||||
'jellyseerr_base_url', 'jellyseerr_api_key',
|
||||
'sonarr_base_url', 'sonarr_api_key', 'radarr_base_url', 'radarr_api_key',
|
||||
'prowlarr_base_url', 'prowlarr_api_key', 'bazarr_base_url', 'bazarr_api_key',
|
||||
'qbittorrent_base_url', 'qbittorrent_username', 'qbittorrent_password',
|
||||
'magent_notify_enabled', 'magent_notify_email_enabled',
|
||||
'magent_notify_email_smtp_host', 'magent_notify_email_smtp_port',
|
||||
'magent_notify_email_smtp_username', 'magent_notify_email_smtp_password',
|
||||
'magent_notify_email_from_address', 'magent_notify_email_from_name',
|
||||
'magent_notify_email_use_tls', 'magent_notify_email_use_ssl',
|
||||
]
|
||||
values = {key.upper(): getattr(runtime, key) for key in keys if getattr(runtime, key, None) is not None}
|
||||
password = secrets.token_urlsafe(30)
|
||||
values.update(
|
||||
APP_NAME='Magent', JWT_SECRET=secrets.token_urlsafe(48),
|
||||
ADMIN_USERNAME='admin', ADMIN_PASSWORD=password,
|
||||
AUTH_COOKIE_SECURE=True, AUTH_COOKIE_DOMAIN='magent.grizzlyflix.co.nz',
|
||||
AUTH_COOKIE_NAME='magent_auth', AUTH_STATE_COOKIE_NAME='magent_logged_in',
|
||||
CORS_ALLOW_ORIGIN='https://magent.grizzlyflix.co.nz',
|
||||
MAGENT_APPLICATION_URL='https://magent.grizzlyflix.co.nz',
|
||||
MAGENT_API_URL='https://magent.grizzlyflix.co.nz/api',
|
||||
SQLITE_PATH='/app/data/magent.db', LOG_FILE='/app/data/magent.log',
|
||||
SITE_BANNER_ENABLED=False, MAGENT_COMING_SOON=True,
|
||||
BACKGROUND_TASKS_ENABLED=False,
|
||||
)
|
||||
destination.mkdir(mode=0o700, parents=True, exist_ok=False)
|
||||
def write_private(name, content):
|
||||
with os.fdopen(os.open(destination / name, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), 'w') as stream:
|
||||
stream.write(content)
|
||||
# Compose single-quoted values preserve dollar signs in SMTP passwords.
|
||||
def encode(value):
|
||||
text = str(value).lower() if isinstance(value, bool) else str(value)
|
||||
if '\n' in text or '\r' in text:
|
||||
raise ValueError('Multiline configuration values require manual review')
|
||||
return "'" + text.replace('\\', '\\\\').replace("'", "\\'") + "'"
|
||||
write_private('.env', ''.join(f'{key}={encode(value)}\n' for key, value in values.items()))
|
||||
write_private('bootstrap-admin.json', json.dumps({'username': 'admin', 'password': password}))
|
||||
print(f'Prepared {len(keys)} allowlisted connection settings; fresh session and admin credentials. No client records copied.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
prepare(Path(sys.argv[1]))
|
||||
@@ -0,0 +1,20 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const { chromium } = require(process.env.REVIEW_PLAYWRIGHT || 'playwright')
|
||||
;(async () => {
|
||||
const browser = await chromium.launch({ headless: true })
|
||||
try {
|
||||
const page = await browser.newPage()
|
||||
await page.route('**/api/**', route => route.fulfill({ json: {} }))
|
||||
await page.goto('http://127.0.0.1:3101/')
|
||||
assert.ok(page.url().endsWith('/coming-soon'))
|
||||
await page.getByRole('heading', { name: 'Your next watch. Made simpler.' }).waitFor()
|
||||
assert.equal(await page.locator('.header').count(), 0)
|
||||
assert.equal(await page.getByRole('link', { name: 'Admin sign in' }).getAttribute('href'), '/login')
|
||||
for (const width of [1440, 390]) {
|
||||
await page.setViewportSize({ width, height: 900 })
|
||||
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth))
|
||||
if (process.env.REVIEW_DIR) await page.screenshot({ path: `${process.env.REVIEW_DIR}/coming-soon-${width}.png`, fullPage: true })
|
||||
}
|
||||
console.log('PASS: cover redirect, isolated layout, admin login link and responsive widths')
|
||||
} finally { await browser.close() }
|
||||
})().catch(error => { console.error(error); process.exitCode = 1 })
|
||||
Reference in New Issue
Block a user