codex-snapshots 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -6
- package/bin/codex-snapshot.mjs +1 -6326
- package/deploy/aliyun/README.md +311 -0
- package/deploy/aliyun/backup-share-data.sh +109 -0
- package/deploy/aliyun/check-ecs-status.sh +149 -0
- package/deploy/aliyun/codex-snapshot-share.env.example +29 -0
- package/deploy/aliyun/codex-snapshot-share.service +26 -0
- package/deploy/aliyun/configure-github-pages-api.sh +141 -0
- package/deploy/aliyun/configure-local-publisher.sh +197 -0
- package/deploy/aliyun/deploy-to-ecs.sh +669 -0
- package/deploy/aliyun/deploy.env.example +52 -0
- package/deploy/aliyun/doctor.mjs +398 -0
- package/deploy/aliyun/install-share-api.sh +252 -0
- package/deploy/aliyun/install-system-deps.sh +84 -0
- package/deploy/aliyun/nginx-codex-snapshots.bootstrap.conf +34 -0
- package/deploy/aliyun/nginx-codex-snapshots.conf +52 -0
- package/deploy/aliyun/preflight.mjs +321 -0
- package/deploy/aliyun/restore-share-data.sh +141 -0
- package/deploy/aliyun/verify-public-share.mjs +404 -0
- package/dist/cli/codex-snapshot.mjs +2654 -0
- package/dist/core/privacy.js +81 -0
- package/dist/core/snapshot.js +1 -0
- package/dist/renderers/markdown.mjs +81 -0
- package/dist/renderers/transcript.js +195 -0
- package/dist/server/http.js +10 -0
- package/dist/server/local-security.js +66 -0
- package/dist/server/local-viewer-app.mjs +1670 -0
- package/dist/server/local-viewer.mjs +210 -0
- package/dist/server/share-api.mjs +1149 -0
- package/dist/server/share-store.js +136 -0
- package/dist/shared/sanitize.js +126 -0
- package/dist/shared/transcript.js +1 -0
- package/dist/sources/index.mjs +2 -0
- package/dist/sources/local-history.mjs +2221 -0
- package/package.json +42 -14
- package/scripts/build-site.mjs +71 -0
- package/scripts/launch-agent.mjs +19 -227
- package/scripts/serve-site.mjs +2 -2
- package/scripts/test-aliyun-deploy-config.sh +230 -0
- package/scripts/test-share-api.mjs +967 -0
- package/scripts/test-site-config.mjs +100 -0
- package/scripts/test-static-site.mjs +403 -0
- package/scripts/write-site-config.mjs +161 -0
- package/server/share-api.mjs +1 -771
- package/site/assets/config.js +3 -0
- package/site/assets/share.js +43 -106
- package/site/assets/site.css +3 -605
- package/site/assets/site.js +15 -92
- package/site/favicon.svg +7 -0
- package/site/index.html +3 -83
- package/site/share/index.html +3 -8
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [[ "${EUID}" -ne 0 ]]; then
|
|
5
|
+
echo "Run as root, for example: sudo DOMAIN=snapshots.example.com $0" >&2
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
DOMAIN="${DOMAIN:-}"
|
|
10
|
+
if [[ -z "${DOMAIN}" ]]; then
|
|
11
|
+
echo "Missing DOMAIN. Example: sudo DOMAIN=snapshots.example.com $0" >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
16
|
+
APP_DIR="${APP_DIR:-/opt/codex-snapshots}"
|
|
17
|
+
ENV_DIR="/etc/codex-snapshots"
|
|
18
|
+
STATE_DIR="/var/lib/codex-snapshots"
|
|
19
|
+
SERVICE_PATH="/etc/systemd/system/codex-snapshot-share.service"
|
|
20
|
+
NGINX_DIR="/etc/nginx/conf.d"
|
|
21
|
+
NGINX_PATH="${NGINX_DIR}/codex-snapshots.conf"
|
|
22
|
+
CERT_DIR="/etc/letsencrypt/live/${DOMAIN}"
|
|
23
|
+
SITE_URL="${SNAPSHOT_SHARE_SITE_URL:-https://ffffhx.github.io/codex-snapshots/}"
|
|
24
|
+
API_URL="${SNAPSHOT_SHARE_PUBLIC_API_URL:-https://${DOMAIN}}"
|
|
25
|
+
TOKEN="${SNAPSHOT_SHARE_TOKEN:-}"
|
|
26
|
+
GITHUB_CLIENT_ID="${SNAPSHOT_GITHUB_CLIENT_ID:-}"
|
|
27
|
+
GITHUB_CLIENT_SECRET="${SNAPSHOT_GITHUB_CLIENT_SECRET:-}"
|
|
28
|
+
GITHUB_OWNER_LOGIN="${SNAPSHOT_GITHUB_OWNER_LOGIN:-${SNAPSHOT_GITHUB_OWNER:-}}"
|
|
29
|
+
GITHUB_OWNER_ID="${SNAPSHOT_GITHUB_OWNER_ID:-}"
|
|
30
|
+
SESSION_SECRET="${SNAPSHOT_SESSION_SECRET:-}"
|
|
31
|
+
AUTH_ALLOWED_ORIGINS="${SNAPSHOT_AUTH_ALLOWED_ORIGINS:-${SITE_URL%/}}"
|
|
32
|
+
SHARE_HOST="${HOST:-127.0.0.1}"
|
|
33
|
+
SHARE_PORT="${PORT:-${SNAPSHOT_SHARE_PORT:-8787}}"
|
|
34
|
+
PROXY_MODE="${SNAPSHOT_SHARE_PROXY_MODE:-auto}"
|
|
35
|
+
PUBLIC_PATH="${SNAPSHOT_SHARE_PUBLIC_PATH:-${SNAPSHOT_SHARE_PROXY_PATH:-}}"
|
|
36
|
+
CADDY_FILE="${SNAPSHOT_SHARE_CADDY_FILE:-/etc/caddy/Caddyfile}"
|
|
37
|
+
GITHUB_AUTH_ENABLED=0
|
|
38
|
+
if [[ -n "${GITHUB_CLIENT_ID}${GITHUB_CLIENT_SECRET}${GITHUB_OWNER_LOGIN}${GITHUB_OWNER_ID}" ]]; then
|
|
39
|
+
GITHUB_AUTH_ENABLED=1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if [[ "${GITHUB_AUTH_ENABLED}" -eq 1 && "${TOKEN}" == "change-me" ]]; then
|
|
43
|
+
TOKEN=""
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [[ -z "${PUBLIC_PATH}" ]]; then
|
|
47
|
+
PUBLIC_PATH="$(node -e 'const url = new URL(process.argv[1]); process.stdout.write(url.pathname === "/" ? "" : url.pathname.replace(/\/+$/, ""));' "${API_URL}")"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if [[ -n "${PUBLIC_PATH}" && "${PUBLIC_PATH}" != /* ]]; then
|
|
51
|
+
PUBLIC_PATH="/${PUBLIC_PATH}"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [[ -z "${TOKEN}" && "${GITHUB_AUTH_ENABLED}" -ne 1 ]]; then
|
|
55
|
+
TOKEN="$(openssl rand -base64 32)"
|
|
56
|
+
echo "Generated SNAPSHOT_SHARE_TOKEN. Save it for your local publisher:"
|
|
57
|
+
echo "${TOKEN}"
|
|
58
|
+
elif [[ -z "${TOKEN}" ]]; then
|
|
59
|
+
echo "No SNAPSHOT_SHARE_TOKEN supplied; GitHub OAuth will authenticate publish/delete."
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [[ -n "${TOKEN}" && ( "${TOKEN}" == *$'\n'* || "${TOKEN}" == *$'\r'* ) ]]; then
|
|
63
|
+
echo "SNAPSHOT_SHARE_TOKEN must be a single-line value." >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if [[ "${GITHUB_AUTH_ENABLED}" -eq 1 ]]; then
|
|
68
|
+
if [[ -z "${GITHUB_CLIENT_ID}" || -z "${GITHUB_CLIENT_SECRET}" ]]; then
|
|
69
|
+
echo "GitHub OAuth needs SNAPSHOT_GITHUB_CLIENT_ID and SNAPSHOT_GITHUB_CLIENT_SECRET." >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
if [[ -z "${GITHUB_OWNER_LOGIN}${GITHUB_OWNER_ID}" ]]; then
|
|
73
|
+
echo "GitHub OAuth needs SNAPSHOT_GITHUB_OWNER_LOGIN or SNAPSHOT_GITHUB_OWNER_ID so the site owner can delete any share." >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
if [[ -z "${SESSION_SECRET}" ]]; then
|
|
77
|
+
SESSION_SECRET="$(openssl rand -base64 48)"
|
|
78
|
+
echo "Generated SNAPSHOT_SESSION_SECRET for GitHub login cookies."
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if ! command -v pnpm >/dev/null 2>&1; then
|
|
83
|
+
echo "pnpm is required to build and install codex-snapshots runtime dependencies." >&2
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [[ ! -f "${REPO_ROOT}/dist/server/share-api.mjs" ]]; then
|
|
88
|
+
(cd "${REPO_ROOT}" && pnpm install --frozen-lockfile && pnpm build)
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
systemd_env_value() {
|
|
92
|
+
local value="$1"
|
|
93
|
+
value="${value//\\/\\\\}"
|
|
94
|
+
value="${value//\"/\\\"}"
|
|
95
|
+
value="${value//\$/\\\$}"
|
|
96
|
+
value="${value//\`/\\\`}"
|
|
97
|
+
printf '"%s"' "${value}"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if ! id codexsnap >/dev/null 2>&1; then
|
|
101
|
+
NOLOGIN_SHELL="/usr/sbin/nologin"
|
|
102
|
+
if [[ ! -x "${NOLOGIN_SHELL}" && -x "/sbin/nologin" ]]; then
|
|
103
|
+
NOLOGIN_SHELL="/sbin/nologin"
|
|
104
|
+
fi
|
|
105
|
+
useradd --system --home-dir "${STATE_DIR}" --shell "${NOLOGIN_SHELL}" codexsnap
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
install -d -m 0755 "${APP_DIR}"
|
|
109
|
+
install -d -m 0750 -o codexsnap -g codexsnap "${STATE_DIR}"
|
|
110
|
+
install -d -m 0755 "${ENV_DIR}"
|
|
111
|
+
install -d -m 0755 "${NGINX_DIR}"
|
|
112
|
+
|
|
113
|
+
rsync -a --delete \
|
|
114
|
+
--exclude ".git" \
|
|
115
|
+
--exclude ".env" \
|
|
116
|
+
--exclude "node_modules" \
|
|
117
|
+
--exclude ".codex-snapshots" \
|
|
118
|
+
--exclude "backups" \
|
|
119
|
+
--exclude "deploy/aliyun/deploy.env" \
|
|
120
|
+
--exclude "*.pem" \
|
|
121
|
+
--exclude "*.key" \
|
|
122
|
+
"${REPO_ROOT}/" "${APP_DIR}/"
|
|
123
|
+
|
|
124
|
+
rm -f "${APP_DIR}/.env" "${APP_DIR}/deploy/aliyun/deploy.env"
|
|
125
|
+
rm -rf "${APP_DIR}/backups"
|
|
126
|
+
|
|
127
|
+
(cd "${APP_DIR}" && pnpm install --prod --frozen-lockfile)
|
|
128
|
+
|
|
129
|
+
{
|
|
130
|
+
printf 'SNAPSHOT_SHARE_TOKEN=%s\n' "$(systemd_env_value "${TOKEN}")"
|
|
131
|
+
printf 'HOST=%s\n' "$(systemd_env_value "${SHARE_HOST}")"
|
|
132
|
+
printf 'PORT=%s\n' "$(systemd_env_value "${SHARE_PORT}")"
|
|
133
|
+
printf 'SNAPSHOT_SHARE_SITE_URL=%s\n' "$(systemd_env_value "${SITE_URL}")"
|
|
134
|
+
printf 'SNAPSHOT_SHARE_PUBLIC_API_URL=%s\n' "$(systemd_env_value "${API_URL}")"
|
|
135
|
+
printf 'SNAPSHOT_SHARE_VIEWER_PATH=%s\n' "$(systemd_env_value "/share/")"
|
|
136
|
+
printf 'SNAPSHOT_SHARE_DATA_FILE=%s\n' "$(systemd_env_value "${STATE_DIR}/shares.json")"
|
|
137
|
+
printf 'SNAPSHOT_SHARE_ALLOW_ANONYMOUS=%s\n' "$(systemd_env_value "false")"
|
|
138
|
+
if [[ -n "${GITHUB_CLIENT_ID}${GITHUB_CLIENT_SECRET}" ]]; then
|
|
139
|
+
printf 'SNAPSHOT_GITHUB_CLIENT_ID=%s\n' "$(systemd_env_value "${GITHUB_CLIENT_ID}")"
|
|
140
|
+
printf 'SNAPSHOT_GITHUB_CLIENT_SECRET=%s\n' "$(systemd_env_value "${GITHUB_CLIENT_SECRET}")"
|
|
141
|
+
printf 'SNAPSHOT_SESSION_SECRET=%s\n' "$(systemd_env_value "${SESSION_SECRET}")"
|
|
142
|
+
printf 'SNAPSHOT_GITHUB_OWNER_LOGIN=%s\n' "$(systemd_env_value "${GITHUB_OWNER_LOGIN}")"
|
|
143
|
+
printf 'SNAPSHOT_GITHUB_OWNER_ID=%s\n' "$(systemd_env_value "${GITHUB_OWNER_ID}")"
|
|
144
|
+
printf 'SNAPSHOT_AUTH_ALLOWED_ORIGINS=%s\n' "$(systemd_env_value "${AUTH_ALLOWED_ORIGINS}")"
|
|
145
|
+
fi
|
|
146
|
+
} > "${ENV_DIR}/share-api.env"
|
|
147
|
+
chmod 0600 "${ENV_DIR}/share-api.env"
|
|
148
|
+
chown root:root "${ENV_DIR}/share-api.env"
|
|
149
|
+
|
|
150
|
+
install -m 0644 "${REPO_ROOT}/deploy/aliyun/codex-snapshot-share.service" "${SERVICE_PATH}"
|
|
151
|
+
|
|
152
|
+
systemctl daemon-reload
|
|
153
|
+
systemctl enable --now codex-snapshot-share.service
|
|
154
|
+
|
|
155
|
+
if [[ "${PROXY_MODE}" == "auto" ]]; then
|
|
156
|
+
if command -v caddy >/dev/null 2>&1 && systemctl is-active --quiet caddy; then
|
|
157
|
+
PROXY_MODE="caddy"
|
|
158
|
+
else
|
|
159
|
+
PROXY_MODE="nginx"
|
|
160
|
+
fi
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
if [[ "${PROXY_MODE}" == "caddy" ]]; then
|
|
164
|
+
if [[ -z "${PUBLIC_PATH}" ]]; then
|
|
165
|
+
echo "SNAPSHOT_SHARE_PUBLIC_PATH is required for Caddy path proxy mode." >&2
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
if [[ ! -f "${CADDY_FILE}" ]]; then
|
|
169
|
+
echo "Caddyfile not found: ${CADDY_FILE}" >&2
|
|
170
|
+
exit 1
|
|
171
|
+
fi
|
|
172
|
+
SNAPSHOT_SHARE_CADDY_FILE="${CADDY_FILE}" \
|
|
173
|
+
SNAPSHOT_SHARE_PUBLIC_PATH="${PUBLIC_PATH}" \
|
|
174
|
+
SNAPSHOT_SHARE_PORT="${SHARE_PORT}" \
|
|
175
|
+
node <<'NODE'
|
|
176
|
+
const fs = require("node:fs");
|
|
177
|
+
|
|
178
|
+
const filePath = process.env.SNAPSHOT_SHARE_CADDY_FILE;
|
|
179
|
+
const publicPath = process.env.SNAPSHOT_SHARE_PUBLIC_PATH.replace(/\/+$/, "");
|
|
180
|
+
const port = process.env.SNAPSHOT_SHARE_PORT;
|
|
181
|
+
const markerStart = "\t# codex-snapshots-share start";
|
|
182
|
+
const markerEnd = "\t# codex-snapshots-share end";
|
|
183
|
+
const route = [
|
|
184
|
+
markerStart,
|
|
185
|
+
`\thandle_path ${publicPath}/* {`,
|
|
186
|
+
`\t\treverse_proxy 127.0.0.1:${port}`,
|
|
187
|
+
"\t}",
|
|
188
|
+
markerEnd,
|
|
189
|
+
].join("\n");
|
|
190
|
+
|
|
191
|
+
let text = fs.readFileSync(filePath, "utf8");
|
|
192
|
+
text = text.replace(/\n\t# codex-snapshots-share start[\s\S]*?\n\t# codex-snapshots-share end\n?/g, "\n");
|
|
193
|
+
|
|
194
|
+
const start = text.indexOf("(room_services) {");
|
|
195
|
+
if (start === -1) {
|
|
196
|
+
throw new Error("Could not find (room_services) block in Caddyfile.");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let depth = 0;
|
|
200
|
+
let insertAt = -1;
|
|
201
|
+
for (let index = start; index < text.length; index += 1) {
|
|
202
|
+
const char = text[index];
|
|
203
|
+
if (char === "{") {
|
|
204
|
+
depth += 1;
|
|
205
|
+
} else if (char === "}") {
|
|
206
|
+
depth -= 1;
|
|
207
|
+
if (depth === 0) {
|
|
208
|
+
insertAt = index;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (insertAt === -1) {
|
|
215
|
+
throw new Error("Could not find end of (room_services) block in Caddyfile.");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
text = `${text.slice(0, insertAt)}${route}\n${text.slice(insertAt)}`;
|
|
219
|
+
fs.writeFileSync(filePath, text);
|
|
220
|
+
NODE
|
|
221
|
+
caddy fmt --overwrite "${CADDY_FILE}" >/dev/null || true
|
|
222
|
+
caddy validate --config "${CADDY_FILE}"
|
|
223
|
+
systemctl reload caddy || systemctl restart caddy
|
|
224
|
+
elif [[ "${PROXY_MODE}" == "nginx" ]]; then
|
|
225
|
+
install -d -m 0755 "${NGINX_DIR}"
|
|
226
|
+
if [[ -f "${CERT_DIR}/fullchain.pem" && -f "${CERT_DIR}/privkey.pem" ]]; then
|
|
227
|
+
NGINX_TEMPLATE="${REPO_ROOT}/deploy/aliyun/nginx-codex-snapshots.conf"
|
|
228
|
+
else
|
|
229
|
+
NGINX_TEMPLATE="${REPO_ROOT}/deploy/aliyun/nginx-codex-snapshots.bootstrap.conf"
|
|
230
|
+
echo "TLS certificate not found at ${CERT_DIR}; installing HTTP bootstrap Nginx config."
|
|
231
|
+
echo "After issuing a certificate, re-run this script to install the HTTPS config."
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
sed "s/snapshots\\.example\\.com/${DOMAIN//\//\\/}/g" "${NGINX_TEMPLATE}" > "${NGINX_PATH}"
|
|
235
|
+
|
|
236
|
+
if command -v nginx >/dev/null 2>&1; then
|
|
237
|
+
nginx -t
|
|
238
|
+
systemctl reload nginx || systemctl restart nginx
|
|
239
|
+
else
|
|
240
|
+
echo "Nginx is not installed yet. Install nginx, then run: nginx -t && systemctl reload nginx"
|
|
241
|
+
fi
|
|
242
|
+
elif [[ "${PROXY_MODE}" == "none" ]]; then
|
|
243
|
+
echo "Skipped reverse proxy configuration."
|
|
244
|
+
else
|
|
245
|
+
echo "Unknown SNAPSHOT_SHARE_PROXY_MODE: ${PROXY_MODE}" >&2
|
|
246
|
+
exit 1
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
echo "Share API service installed."
|
|
250
|
+
echo "Local health check: curl http://${SHARE_HOST}:${SHARE_PORT}/api/snapshots/health"
|
|
251
|
+
echo "Public health check: curl ${API_URL%/}/api/snapshots/health"
|
|
252
|
+
echo "Set GitHub repository variable CODEX_SNAPSHOTS_PUBLIC_API_URL=${API_URL}"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [[ "${EUID}" -ne 0 ]]; then
|
|
5
|
+
echo "Run as root, for example: sudo $0" >&2
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
NODE_MAJOR="${NODE_MAJOR:-20}"
|
|
10
|
+
PROXY_MODE="${SNAPSHOT_SHARE_PROXY_MODE:-${PROXY_MODE:-nginx}}"
|
|
11
|
+
|
|
12
|
+
has_command() {
|
|
13
|
+
command -v "$1" >/dev/null 2>&1
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
node_is_supported() {
|
|
17
|
+
has_command node && node -e 'const major=Number(process.versions.node.split(".")[0]); process.exit(major >= 18 ? 0 : 1);' >/dev/null 2>&1
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
install_debian_deps() {
|
|
21
|
+
apt-get update
|
|
22
|
+
apt-get install -y ca-certificates curl gnupg git openssl rsync
|
|
23
|
+
|
|
24
|
+
if [[ "${PROXY_MODE}" != "caddy" && "${PROXY_MODE}" != "none" ]]; then
|
|
25
|
+
apt-get install -y nginx certbot python3-certbot-nginx
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if ! node_is_supported; then
|
|
29
|
+
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
|
|
30
|
+
apt-get install -y nodejs
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
install_rhel_deps() {
|
|
35
|
+
local manager="$1"
|
|
36
|
+
|
|
37
|
+
"${manager}" install -y ca-certificates curl git openssl rsync
|
|
38
|
+
if [[ "${PROXY_MODE}" != "caddy" && "${PROXY_MODE}" != "none" ]]; then
|
|
39
|
+
"${manager}" install -y nginx
|
|
40
|
+
"${manager}" install -y certbot python3-certbot-nginx || {
|
|
41
|
+
"${manager}" install -y epel-release || true
|
|
42
|
+
"${manager}" install -y certbot python3-certbot-nginx || "${manager}" install -y certbot
|
|
43
|
+
}
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if ! node_is_supported; then
|
|
47
|
+
curl -fsSL "https://rpm.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
|
|
48
|
+
"${manager}" install -y nodejs
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if has_command apt-get; then
|
|
53
|
+
install_debian_deps
|
|
54
|
+
elif has_command dnf; then
|
|
55
|
+
install_rhel_deps dnf
|
|
56
|
+
elif has_command yum; then
|
|
57
|
+
install_rhel_deps yum
|
|
58
|
+
else
|
|
59
|
+
echo "Unsupported package manager. Install Node.js 18+, Nginx, Certbot, Git, OpenSSL, and rsync manually." >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if ! node_is_supported; then
|
|
64
|
+
echo "Node.js 18+ is required, but the installed node is: $(node --version 2>/dev/null || echo missing)" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [[ "${PROXY_MODE}" != "caddy" && "${PROXY_MODE}" != "none" ]]; then
|
|
69
|
+
systemctl enable --now nginx >/dev/null 2>&1 || true
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo "Installed/verified dependencies:"
|
|
73
|
+
echo " node: $(node --version)"
|
|
74
|
+
if command -v nginx >/dev/null 2>&1; then
|
|
75
|
+
echo " nginx: $(nginx -v 2>&1)"
|
|
76
|
+
else
|
|
77
|
+
echo " nginx: skipped for ${PROXY_MODE} proxy mode"
|
|
78
|
+
fi
|
|
79
|
+
if command -v certbot >/dev/null 2>&1; then
|
|
80
|
+
echo " certbot: $(certbot --version 2>&1)"
|
|
81
|
+
else
|
|
82
|
+
echo " certbot: skipped for ${PROXY_MODE} proxy mode"
|
|
83
|
+
fi
|
|
84
|
+
echo " rsync: $(rsync --version | head -n 1)"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Replace snapshots.example.com with your real domain.
|
|
2
|
+
# Use this before the first TLS certificate is issued.
|
|
3
|
+
|
|
4
|
+
map $http_upgrade $connection_upgrade {
|
|
5
|
+
default upgrade;
|
|
6
|
+
'' close;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
server {
|
|
10
|
+
listen 80;
|
|
11
|
+
listen [::]:80;
|
|
12
|
+
server_name snapshots.example.com;
|
|
13
|
+
|
|
14
|
+
client_max_body_size 80m;
|
|
15
|
+
|
|
16
|
+
location /.well-known/acme-challenge/ {
|
|
17
|
+
root /var/www/html;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
location / {
|
|
21
|
+
proxy_pass http://127.0.0.1:8787;
|
|
22
|
+
proxy_http_version 1.1;
|
|
23
|
+
proxy_set_header Host $host;
|
|
24
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
25
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
26
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
27
|
+
proxy_set_header X-Forwarded-Port $server_port;
|
|
28
|
+
proxy_set_header X-Forwarded-Proto http;
|
|
29
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
30
|
+
proxy_set_header Connection $connection_upgrade;
|
|
31
|
+
proxy_read_timeout 120s;
|
|
32
|
+
proxy_send_timeout 120s;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Replace snapshots.example.com with your real domain.
|
|
2
|
+
# This file assumes TLS certificates are managed by certbot at /etc/letsencrypt/live/<domain>/.
|
|
3
|
+
|
|
4
|
+
map $http_upgrade $connection_upgrade {
|
|
5
|
+
default upgrade;
|
|
6
|
+
'' close;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
server {
|
|
10
|
+
listen 80;
|
|
11
|
+
listen [::]:80;
|
|
12
|
+
server_name snapshots.example.com;
|
|
13
|
+
|
|
14
|
+
location /.well-known/acme-challenge/ {
|
|
15
|
+
root /var/www/html;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
location / {
|
|
19
|
+
return 301 https://$host$request_uri;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
server {
|
|
24
|
+
listen 443 ssl http2;
|
|
25
|
+
listen [::]:443 ssl http2;
|
|
26
|
+
server_name snapshots.example.com;
|
|
27
|
+
|
|
28
|
+
ssl_certificate /etc/letsencrypt/live/snapshots.example.com/fullchain.pem;
|
|
29
|
+
ssl_certificate_key /etc/letsencrypt/live/snapshots.example.com/privkey.pem;
|
|
30
|
+
|
|
31
|
+
client_max_body_size 80m;
|
|
32
|
+
|
|
33
|
+
add_header X-Content-Type-Options nosniff always;
|
|
34
|
+
add_header X-Frame-Options DENY always;
|
|
35
|
+
add_header Referrer-Policy no-referrer-when-downgrade always;
|
|
36
|
+
add_header Strict-Transport-Security "max-age=31536000" always;
|
|
37
|
+
|
|
38
|
+
location / {
|
|
39
|
+
proxy_pass http://127.0.0.1:8787;
|
|
40
|
+
proxy_http_version 1.1;
|
|
41
|
+
proxy_set_header Host $host;
|
|
42
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
43
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
44
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
45
|
+
proxy_set_header X-Forwarded-Port $server_port;
|
|
46
|
+
proxy_set_header X-Forwarded-Proto https;
|
|
47
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
48
|
+
proxy_set_header Connection $connection_upgrade;
|
|
49
|
+
proxy_read_timeout 120s;
|
|
50
|
+
proxy_send_timeout 120s;
|
|
51
|
+
}
|
|
52
|
+
}
|