-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsetup.ps1
More file actions
134 lines (122 loc) · 7.98 KB
/
Copy pathsetup.ps1
File metadata and controls
134 lines (122 loc) · 7.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<#
Space Works - Open Source Makerspace Manager first-run setup for self-hosting (Windows).
Right-click -> "Run with PowerShell", or run: powershell -ExecutionPolicy Bypass -File setup.ps1
#>
$ErrorActionPreference = "Stop"
Set-Location -Path $PSScriptRoot
$compose = @("compose", "-f", "docker-compose.prod.yml", "-f", "docker/compose.build.yml")
function Say ($m) { Write-Host "`n$m" -ForegroundColor Cyan }
function Warn ($m) { Write-Host $m -ForegroundColor Yellow }
function Die ($m) { Write-Host "ERROR: $m" -ForegroundColor Red; exit 1 }
# 1. Docker must be installed and running.
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Die "Docker is not installed. Install Docker Desktop first: https://www.docker.com/products/docker-desktop/"
}
docker info *> $null
if ($LASTEXITCODE -ne 0) { Die "Docker is installed but not running. Start Docker Desktop, then run this again." }
function New-Key([int]$len = 50) {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
$bytes = New-Object byte[] $len
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
-join ($bytes | ForEach-Object { $chars[$_ % $chars.Length] })
}
function New-FernetKey() {
$bytes = New-Object byte[] 32
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
[Convert]::ToBase64String($bytes).Replace("+", "-").Replace("/", "_")
}
$firstRun = $false
if (Test-Path ".env") {
Say "Found an existing .env - keeping your settings and secrets."
}
else {
$firstRun = $true
Say "Welcome! Let's set up Space Works. Press Enter to accept the [default]."
$webaddr = Read-Host "Web address (host name or IP, no http://) [localhost]"; if (-not $webaddr) { $webaddr = "localhost" }
# Normalize: strip any scheme, path, and port so ALLOWED_HOSTS/CORS are valid.
$webhost = ($webaddr -replace '^[a-zA-Z][a-zA-Z0-9+.-]*://', '' -replace '/.*$', '' -replace ':.*$', '')
if (-not $webhost) { $webhost = "localhost" }
$msname = Read-Host "Name of your makerspace [My Makerspace]"; if (-not $msname) { $msname = "My Makerspace" }
$adminUser = Read-Host "Admin login username [admin]"; if (-not $adminUser) { $adminUser = "admin" }
$adminEmail = Read-Host "Admin email [admin@example.com]"; if (-not $adminEmail) { $adminEmail = "admin@example.com" }
$adminPass = [System.Net.NetworkCredential]::new("", (Read-Host "Admin password (leave blank to auto-generate)" -AsSecureString)).Password
$genPass = $false
if (-not $adminPass) { $adminPass = (New-Key 16); $genPass = $true }
$stripeSecretKey = Read-Host "Stripe secret key (optional; leave blank to skip)"
$stripeWebhookSecret = [System.Net.NetworkCredential]::new("", (Read-Host "Stripe webhook secret (optional; leave blank to skip)" -AsSecureString)).Password
$stripeDefaultCurrency = Read-Host "Stripe default currency [usd]"; if (-not $stripeDefaultCurrency) { $stripeDefaultCurrency = "usd" }
if (($stripeSecretKey -and -not $stripeWebhookSecret) -or (-not $stripeSecretKey -and $stripeWebhookSecret)) {
Warn "Stripe needs both secrets; leaving payments unconfigured."
$stripeSecretKey = ""; $stripeWebhookSecret = ""
}
Say "Writing .env (secrets generated automatically)..."
$envText = @"
# Generated by setup.ps1 - keep this file private; it holds your secrets.
POSTGRES_PASSWORD=$(New-Key 32)
MINIO_ROOT_USER=$(New-Key 24)
MINIO_ROOT_PASSWORD=$(New-Key 40)
SECRET_KEY=$(New-Key 50)
API_CLIENT_ENC_KEY=$(New-FernetKey)
ALLOWED_HOSTS=$webhost,localhost,127.0.0.1,backend
CORS_ALLOWED_ORIGINS=http://$webhost
# Absolute base for links in outbound email (password reset, invitations). Without it
# those links are emitted as bare paths like "/reset-password?..." and are unclickable.
PUBLIC_APP_BASE_URL=http://$webhost
HTTP_PORT=80
ENABLE_HTTPS=false
"@
# Write UTF-8 without BOM so docker compose parses the first variable correctly.
[System.IO.File]::WriteAllText((Join-Path $PSScriptRoot ".env"), $envText, (New-Object System.Text.UTF8Encoding($false)))
}
Say "Building and starting the app (first run can take a few minutes)..."
docker @compose up -d --build
if ($LASTEXITCODE -ne 0) { Die "docker compose failed to start. See the output above." }
Say "Waiting for the app to be ready..."
$ready = $false
for ($i = 0; $i -lt 60; $i++) {
docker @compose exec -T backend python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/v1/health/readiness/', timeout=3)" *> $null
if ($LASTEXITCODE -eq 0) { $ready = $true; break }
Start-Sleep -Seconds 3
}
if (-not $ready) { Die "The app did not become ready in time. Check logs with: docker $($compose -join ' ') logs backend" }
if ($firstRun) {
Say "Creating your admin account and makerspace..."
docker @compose exec -T backend python manage.py setup_instance --username $adminUser --email $adminEmail --password $adminPass --makerspace-name $msname
if ($LASTEXITCODE -ne 0) { Die "Could not create the admin account. See the output above." }
if ($stripeSecretKey) {
$env:SETUP_STRIPE_SECRET_KEY = $stripeSecretKey
$env:SETUP_STRIPE_WEBHOOK_SECRET = $stripeWebhookSecret
$env:SETUP_STRIPE_DEFAULT_CURRENCY = $stripeDefaultCurrency
$env:SETUP_MAKERSPACE_NAME = $msname
docker @compose exec -T -e SETUP_STRIPE_SECRET_KEY -e SETUP_STRIPE_WEBHOOK_SECRET -e SETUP_STRIPE_DEFAULT_CURRENCY -e SETUP_MAKERSPACE_NAME backend python manage.py shell -c 'import os; from django.utils.text import slugify; from apps.makerspaces.models import Makerspace; from apps.payments.models import MakerspacePaymentSettings; makerspace = Makerspace.objects.get(slug=slugify(os.environ["SETUP_MAKERSPACE_NAME"])); settings = MakerspacePaymentSettings.for_makerspace(makerspace); settings.set_stripe_secret_key(os.environ["SETUP_STRIPE_SECRET_KEY"]); settings.set_stripe_webhook_secret(os.environ["SETUP_STRIPE_WEBHOOK_SECRET"]); settings.default_currency = os.environ["SETUP_STRIPE_DEFAULT_CURRENCY"]; settings.save()'
Remove-Item Env:SETUP_STRIPE_SECRET_KEY, Env:SETUP_STRIPE_WEBHOOK_SECRET, Env:SETUP_STRIPE_DEFAULT_CURRENCY, Env:SETUP_MAKERSPACE_NAME
if ($LASTEXITCODE -ne 0) { Die "Could not save Stripe settings. See the output above." }
}
$autoUpdate = Read-Host "Enable automatic production updates from main? [Y/n]"
if (-not $autoUpdate -or $autoUpdate -match '^[Yy]') {
try { & (Join-Path $PSScriptRoot "scripts\install-auto-update.ps1") }
catch { Warn "Could not install the seven-day updater: $($_.Exception.Message) Run scripts\install-auto-update.ps1 later." }
}
else {
try {
& (Join-Path $PSScriptRoot "scripts\install-auto-update.ps1")
docker @compose exec -T backend python manage.py update_control set-auto off *> $null
if ($LASTEXITCODE -ne 0) { throw "The update preference could not be saved." }
Warn "Automatic installation is off. The host will still check for releases so Update now works from Platform settings."
}
catch { Warn "Could not install the seven-day update checker: $($_.Exception.Message) Run scripts\install-auto-update.ps1 later, then turn automatic updates off in Platform settings." }
}
$port = (Select-String -Path ".env" -Pattern '^HTTP_PORT=(.*)$').Matches.Groups[1].Value; if (-not $port) { $port = "80" }
$suffix = ""; if ($port -ne "80") { $suffix = ":$port" }
Say "All done!"
Write-Host " Public catalog : http://$webhost$suffix/"
Write-Host " Staff console : http://$webhost$suffix/admin (React, username: $adminUser)"
Write-Host " Control plane : /control/ on the backend only; not published on the public port"
if ($genPass) { Warn " Generated admin password: $adminPass (save this now)" }
Write-Host ""
Write-Host "Next: log into the React staff console at /admin, add inventory, and turn on 'public inventory' for your makerspace."
}
else {
Say "App started. To create the first admin (only if you haven't yet), run:"
Write-Host " docker $($compose -join ' ') exec backend python manage.py setup_instance --username admin --password 'a-strong-password' --makerspace-name 'My Makerspace'"
}