1749e73e50
gates / gates (push) Successful in 27s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
191 lines
8.3 KiB
Python
191 lines
8.3 KiB
Python
# The household's first setup of each app, THROUGH the gate (its browser holds the gate cookies from `ro.py household`).
|
|
# Each app's own first-run API, as its first-setup screen calls it. Passwords from ro_creds.json; never printed.
|
|
import json, sys, time
|
|
sys.path.insert(0, '.')
|
|
import ro
|
|
from ro import w, host
|
|
|
|
|
|
def J(b, app, method, path, body=None, headers=None):
|
|
st, text, _ = b.req(f"https://{host(app)}{path}", method, body=body, accept="application/json", headers=headers)
|
|
return st, text
|
|
|
|
|
|
def actualbudget(b, c):
|
|
return J(b, "actualbudget", "POST", "/account/bootstrap", {"password": c["pw"]})
|
|
|
|
|
|
def komga(b, c):
|
|
return J(b, "komga", "POST", "/api/v1/claim", headers={"X-Komga-Email": c["email"], "X-Komga-Password": c["pw"]})
|
|
|
|
|
|
def jellyfin(b, c):
|
|
out = []
|
|
out.append(J(b, "jellyfin", "POST", "/Startup/Configuration", {"UICulture": "en-US", "MetadataCountryCode": "HU", "PreferredMetadataLanguage": "hu"})[0])
|
|
out.append(J(b, "jellyfin", "GET", "/Startup/User")[0])
|
|
out.append(J(b, "jellyfin", "POST", "/Startup/User", {"Name": c["user"], "Password": c["pw"]})[0])
|
|
st, t = J(b, "jellyfin", "POST", "/Startup/Complete")
|
|
out.append(st)
|
|
return st, f"steps {out}"
|
|
|
|
|
|
def emby(b, c):
|
|
out = [J(b, "emby", "POST", "/emby/Startup/Configuration", {"UICulture": "en-US", "MetadataCountryCode": "HU", "PreferredMetadataLanguage": "hu"})[0],
|
|
J(b, "emby", "POST", "/emby/Startup/User", {"Name": c["user"], "Password": c["pw"]})[0]]
|
|
st, t = J(b, "emby", "POST", "/emby/Startup/Complete")
|
|
out.append(st)
|
|
return st, f"steps {out}"
|
|
|
|
|
|
def navidrome(b, c):
|
|
return J(b, "navidrome", "POST", "/auth/createAdmin", {"username": c["user"], "password": c["pw"]})
|
|
|
|
|
|
def ghost(b, c):
|
|
return J(b, "ghost", "POST", "/ghost/api/admin/authentication/setup/",
|
|
{"setup": [{"name": "Family", "email": c["email"], "password": c["pw"] + "Zz9", "blogTitle": "Family"}]},
|
|
headers={"Origin": f"https://{host('ghost')}"})
|
|
|
|
|
|
def home_assistant(b, c):
|
|
return J(b, "home-assistant", "POST", "/api/onboarding/users",
|
|
{"client_id": f"https://{host('home-assistant')}/", "name": "Family", "username": c["user"], "password": c["pw"], "language": "en"})
|
|
|
|
|
|
def romm(b, c):
|
|
J(b, "romm", "GET", "/api/heartbeat") # sets romm's csrftoken cookie, as its setup screen's first call does
|
|
tok = b.jar.get(host("romm"), {}).get("romm_csrftoken") or b.jar.get(host("romm"), {}).get("csrftoken", "")
|
|
return J(b, "romm", "POST", "/api/users", {"username": c["user"], "password": c["pw"], "email": c["email"], "role": "admin"},
|
|
headers={"X-CSRFToken": tok})
|
|
|
|
|
|
def zipline(b, c):
|
|
return J(b, "zipline", "POST", "/api/setup", {"username": c["user"], "password": c["pw"]})
|
|
|
|
|
|
def docmost(b, c):
|
|
return J(b, "docmost", "POST", "/api/auth/setup", {"name": "Family", "email": c["email"], "password": c["pw"], "workspaceName": "Family"})
|
|
|
|
|
|
def calcom(b, c):
|
|
return J(b, "calcom", "POST", "/api/auth/setup", {"username": c["user"], "email_address": c["email"], "full_name": "Family", "password": c["pw"] + "Aa1!"})
|
|
|
|
|
|
def _form(b, app, path, fields, csrf_name):
|
|
import re, urllib.parse
|
|
st, html, _ = b.req(f"https://{host(app)}{path}")
|
|
m = re.search(r'name="' + csrf_name + r'" value="([^"]+)"', html)
|
|
fields[csrf_name] = m.group(1) if m else ""
|
|
body = urllib.parse.urlencode(fields)
|
|
st, text, hops = b.req(f"https://{host(app)}{path}", "POST", body=body,
|
|
headers={"Content-Type": "application/x-www-form-urlencoded", "Referer": f"https://{host(app)}{path}", "Origin": f"https://{host(app)}"})
|
|
return st, f"after POST: {' -> '.join(str(h[0]) + ' ' + h[2] for h in hops)}"
|
|
|
|
|
|
def gitea(b, c):
|
|
import re
|
|
st, html, _ = b.req(f"https://{host('gitea')}/")
|
|
fields = dict(re.findall(r'<input[^>]*name="([^"]+)"[^>]*value="([^"]*)"', html))
|
|
for sel in re.findall(r'<select[^>]*name="([^"]+)"', html):
|
|
fields.setdefault(sel, "")
|
|
fields.update({"db_type": fields.get("db_type") or "sqlite3", "admin_name": "family", "admin_email": c["email"],
|
|
"admin_passwd": c["pw"], "admin_confirm_passwd": c["pw"]})
|
|
import urllib.parse
|
|
st, text, hops = b.req(f"https://{host('gitea')}/", "POST", body=urllib.parse.urlencode(fields),
|
|
headers={"Content-Type": "application/x-www-form-urlencoded"})
|
|
return st, f"after POST: {' -> '.join(str(h[0]) + ' ' + h[2] for h in hops)}"
|
|
|
|
|
|
def tandoor(b, c):
|
|
return _form(b, "tandoor", "/setup/", {"name": c["user"], "password": c["pw"], "password_confirm": c["pw"]}, "csrfmiddlewaretoken")
|
|
|
|
|
|
def homebox(b, c):
|
|
return J(b, "homebox", "POST", "/api/v1/users/register", {"name": "Family", "email": c["email"], "password": c["pw"]})
|
|
|
|
|
|
def papra(b, c):
|
|
return J(b, "papra", "POST", "/api/auth/sign-up/email", {"email": c["email"], "password": c["pw"], "name": "Family"},
|
|
headers={"Origin": f"https://{host('papra')}"})
|
|
|
|
|
|
def sparkyfitness(b, c):
|
|
return J(b, "sparkyfitness", "POST", "/api/auth/register", {"email": c["email"], "password": c["pw"], "full_name": "Family"})
|
|
|
|
|
|
def vikunja(b, c):
|
|
return J(b, "vikunja", "POST", "/api/v1/register", {"username": c["user"], "email": c["email"], "password": c["pw"]})
|
|
|
|
|
|
def adventurelog(b, c):
|
|
J(b, "adventurelog", "GET", "/auth/browser/v1/config")
|
|
tok = b.jar.get(host("adventurelog"), {}).get("csrftoken", "")
|
|
return J(b, "adventurelog", "POST", "/auth/browser/v1/auth/signup", {"username": c["user"], "email": c["email"], "password": c["pw"]},
|
|
headers={"X-CSRFToken": tok, "Referer": f"https://{host('adventurelog')}/", "Origin": f"https://{host('adventurelog')}"})
|
|
|
|
|
|
def termix(b, c):
|
|
return J(b, "termix", "POST", "/users/create", {"username": c["user"], "password": c["pw"]})
|
|
|
|
|
|
def opengist(b, c):
|
|
return _form(b, "opengist", "/-/register", {"username": c["user"], "password": c["pw"]}, "_csrf")
|
|
|
|
|
|
def _arr(b, app, c):
|
|
import re
|
|
st, html, _ = b.req(f"https://{host(app)}/initialize.json", accept="application/json")
|
|
key = json.loads(html).get("apiKey", "")
|
|
h = {"X-Api-Key": key}
|
|
st, cfg = J(b, app, "GET", "/api/v3/config/host", headers=h)
|
|
cfg = json.loads(cfg)
|
|
cfg.update({"authenticationMethod": "forms", "authenticationRequired": "enabled", "username": c["user"],
|
|
"password": c["pw"], "passwordConfirmation": c["pw"]})
|
|
st, t = J(b, app, "PUT", "/api/v3/config/host", cfg, headers=h)
|
|
return st, t[:40].replace(key, "<key>")
|
|
|
|
|
|
def radarr(b, c):
|
|
return _arr(b, "radarr", c)
|
|
|
|
|
|
def sonarr(b, c):
|
|
return _arr(b, "sonarr", c)
|
|
|
|
|
|
def gramps_web(b, c):
|
|
st, t = J(b, "gramps-web", "GET", "/api/token/create_owner/")
|
|
tok = json.loads(t).get("access_token", "") if st == 200 else ""
|
|
st, t = J(b, "gramps-web", "POST", "/api/users/" + c["user"] + "/create_owner/", {"password": c["pw"], "email": c["email"], "full_name": "Family"},
|
|
headers={"Authorization": "Bearer " + tok})
|
|
return st, t
|
|
|
|
|
|
def wishlist(b, c):
|
|
import urllib.parse
|
|
body = urllib.parse.urlencode({"name": "Family", "username": c["user"], "email": c["email"], "password": c["pw"], "confirmPassword": c["pw"]})
|
|
st, t, _ = b.req(f"https://{host('wishlist')}/signup", "POST", body=body, accept="application/json",
|
|
headers={"Content-Type": "application/x-www-form-urlencoded", "Origin": f"https://{host('wishlist')}", "x-sveltekit-action": "true"})
|
|
return st, t
|
|
|
|
|
|
FN = {"actualbudget": actualbudget, "komga": komga, "jellyfin": jellyfin, "emby": emby, "navidrome": navidrome,
|
|
"ghost": ghost, "home-assistant": home_assistant, "romm": romm,
|
|
"zipline": zipline, "docmost": docmost, "calcom": calcom, "gitea": gitea, "tandoor": tandoor,
|
|
"homebox": homebox, "papra": papra, "sparkyfitness": sparkyfitness, "vikunja": vikunja, "adventurelog": adventurelog,
|
|
"termix": termix, "opengist": opengist, "radarr": radarr, "sonarr": sonarr, "gramps-web": gramps_web, "wishlist": wishlist}
|
|
|
|
if __name__ == "__main__":
|
|
w.login()
|
|
b = ro.household()
|
|
for app in sys.argv[1:]:
|
|
c = ro.creds(app)
|
|
try:
|
|
st, text = FN[app](b, c)
|
|
except Exception as e:
|
|
st, text = "ERR", str(e)
|
|
for v in (c["pw"],):
|
|
text = str(text).replace(v, "<pw>")
|
|
w.say(app, "HOUSEHOLD SETUP through the gate ->", st, text[:120])
|
|
ro.save(b)
|