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.
Files changed (51) hide show
  1. package/README.md +101 -6
  2. package/bin/codex-snapshot.mjs +1 -6326
  3. package/deploy/aliyun/README.md +311 -0
  4. package/deploy/aliyun/backup-share-data.sh +109 -0
  5. package/deploy/aliyun/check-ecs-status.sh +149 -0
  6. package/deploy/aliyun/codex-snapshot-share.env.example +29 -0
  7. package/deploy/aliyun/codex-snapshot-share.service +26 -0
  8. package/deploy/aliyun/configure-github-pages-api.sh +141 -0
  9. package/deploy/aliyun/configure-local-publisher.sh +197 -0
  10. package/deploy/aliyun/deploy-to-ecs.sh +669 -0
  11. package/deploy/aliyun/deploy.env.example +52 -0
  12. package/deploy/aliyun/doctor.mjs +398 -0
  13. package/deploy/aliyun/install-share-api.sh +252 -0
  14. package/deploy/aliyun/install-system-deps.sh +84 -0
  15. package/deploy/aliyun/nginx-codex-snapshots.bootstrap.conf +34 -0
  16. package/deploy/aliyun/nginx-codex-snapshots.conf +52 -0
  17. package/deploy/aliyun/preflight.mjs +321 -0
  18. package/deploy/aliyun/restore-share-data.sh +141 -0
  19. package/deploy/aliyun/verify-public-share.mjs +404 -0
  20. package/dist/cli/codex-snapshot.mjs +2654 -0
  21. package/dist/core/privacy.js +81 -0
  22. package/dist/core/snapshot.js +1 -0
  23. package/dist/renderers/markdown.mjs +81 -0
  24. package/dist/renderers/transcript.js +195 -0
  25. package/dist/server/http.js +10 -0
  26. package/dist/server/local-security.js +66 -0
  27. package/dist/server/local-viewer-app.mjs +1670 -0
  28. package/dist/server/local-viewer.mjs +210 -0
  29. package/dist/server/share-api.mjs +1149 -0
  30. package/dist/server/share-store.js +136 -0
  31. package/dist/shared/sanitize.js +126 -0
  32. package/dist/shared/transcript.js +1 -0
  33. package/dist/sources/index.mjs +2 -0
  34. package/dist/sources/local-history.mjs +2221 -0
  35. package/package.json +42 -14
  36. package/scripts/build-site.mjs +71 -0
  37. package/scripts/launch-agent.mjs +19 -227
  38. package/scripts/serve-site.mjs +2 -2
  39. package/scripts/test-aliyun-deploy-config.sh +230 -0
  40. package/scripts/test-share-api.mjs +967 -0
  41. package/scripts/test-site-config.mjs +100 -0
  42. package/scripts/test-static-site.mjs +403 -0
  43. package/scripts/write-site-config.mjs +161 -0
  44. package/server/share-api.mjs +1 -771
  45. package/site/assets/config.js +3 -0
  46. package/site/assets/share.js +43 -106
  47. package/site/assets/site.css +3 -605
  48. package/site/assets/site.js +15 -92
  49. package/site/favicon.svg +7 -0
  50. package/site/index.html +3 -83
  51. 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
+ }