mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Improve env generation script, extend push service, fix workspace venv settings
This commit is contained in:
+2
-12
@@ -1,12 +1,2 @@
|
|||||||
# Exclude data directory to prevent local database from being copied into production images
|
# Firebase cert: bind-mounted at runtime; do not send to docker build context
|
||||||
backend/data/
|
firebase-cert.json
|
||||||
|
|
||||||
# Exclude logs
|
|
||||||
backend/logs/
|
|
||||||
|
|
||||||
# Exclude development files
|
|
||||||
node_modules/
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
README.md
|
|
||||||
*.log
|
|
||||||
|
|||||||
@@ -118,6 +118,12 @@ web_modules/
|
|||||||
.env.production.local
|
.env.production.local
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
|
# Firebase service account JSON (backend/firebase-cert.json; bind-mounted in docker-compose)
|
||||||
|
firebase-cert.json
|
||||||
|
**/firebase-cert.json
|
||||||
|
firebase-adminsdk.json
|
||||||
|
**/firebase-adminsdk.json
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
.cache
|
.cache
|
||||||
.parcel-cache
|
.parcel-cache
|
||||||
|
|||||||
Vendored
+1
@@ -7,5 +7,6 @@
|
|||||||
"**/.venv": true,
|
"**/.venv": true,
|
||||||
"**/node_modules": true
|
"**/node_modules": true
|
||||||
},
|
},
|
||||||
|
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
|
||||||
"python.terminal.activateEnvironment": false
|
"python.terminal.activateEnvironment": false
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from pywebpush import webpush, WebPushException
|
from pywebpush import webpush, WebPushException
|
||||||
@@ -8,24 +9,45 @@ from .models import PushSubscription, User, Message, DMEnvelope, FcmToken
|
|||||||
import firebase_admin
|
import firebase_admin
|
||||||
from firebase_admin import credentials as firebase_credentials
|
from firebase_admin import credentials as firebase_credentials
|
||||||
from firebase_admin import messaging as firebase_messaging
|
from firebase_admin import messaging as firebase_messaging
|
||||||
import base64
|
|
||||||
|
|
||||||
logger = logging.getLogger("uvicorn.error")
|
logger = logging.getLogger("uvicorn.error")
|
||||||
|
|
||||||
|
|
||||||
|
def _load_firebase_service_account_dict(firebase_cert: str) -> dict:
|
||||||
|
"""Load Firebase service account JSON from FIREBASE_CERT path (relative to process cwd, e.g. backend/)."""
|
||||||
|
s = (firebase_cert or "").strip()
|
||||||
|
if not s:
|
||||||
|
raise RuntimeError("FIREBASE_CERT env variable is required (path to service account JSON file)")
|
||||||
|
|
||||||
|
p = Path(s).expanduser()
|
||||||
|
if not p.is_absolute():
|
||||||
|
p = Path.cwd() / p
|
||||||
|
if not p.is_file():
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"FIREBASE_CERT is not a readable file: {p} (set FIREBASE_CERT to the JSON key path)"
|
||||||
|
)
|
||||||
|
|
||||||
|
with p.open(encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
if not isinstance(data, dict) or data.get("type") != "service_account":
|
||||||
|
raise ValueError("Firebase credentials file must be a service account JSON object")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class PushNotificationService:
|
class PushNotificationService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.vapid_private_key = os.getenv("VAPID_PRIVATE_KEY")
|
self.vapid_private_key = os.getenv("VAPID_PRIVATE_KEY")
|
||||||
self.vapid_public_key = os.getenv("VAPID_PUBLIC_KEY")
|
self.vapid_public_key = os.getenv("VAPID_PUBLIC_KEY")
|
||||||
# Firebase Admin initialization (modern API). Only FIREBASE_CERT env is supported.
|
# Firebase Admin is required for main (FCM). FIREBASE_CERT = path to service account JSON.
|
||||||
self.firebase_initialized = False
|
self.firebase_initialized = False
|
||||||
|
firebase_cert = os.getenv("FIREBASE_CERT")
|
||||||
|
if not (firebase_cert or "").strip():
|
||||||
|
raise RuntimeError(
|
||||||
|
"FIREBASE_CERT is required (path to Firebase service account JSON); "
|
||||||
|
"docker-compose sets this and bind-mounts backend/firebase-cert.json"
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
firebase_cert = os.getenv("FIREBASE_CERT")
|
sa_dict = _load_firebase_service_account_dict(firebase_cert)
|
||||||
if not firebase_cert:
|
|
||||||
raise RuntimeError("FIREBASE_CERT env variable is required for Firebase Admin SDK initialization")
|
|
||||||
|
|
||||||
# Support raw JSON or base64-encoded JSON in FIREBASE_CERT
|
|
||||||
decoded = base64.b64decode(firebase_cert).decode("utf-8")
|
|
||||||
sa_dict = json.loads(decoded)
|
|
||||||
|
|
||||||
cred = firebase_credentials.Certificate(sa_dict)
|
cred = firebase_credentials.Certificate(sa_dict)
|
||||||
firebase_admin.initialize_app(cred)
|
firebase_admin.initialize_app(cred)
|
||||||
|
|||||||
@@ -56,12 +56,13 @@ FILE_STORAGE_DB_PASSWORD=separate_password_for_file_storage
|
|||||||
JWT_SECRET=your_jwt_secret_key
|
JWT_SECRET=your_jwt_secret_key
|
||||||
VAPID_PUBLIC_KEY=generated_vapid_public_key
|
VAPID_PUBLIC_KEY=generated_vapid_public_key
|
||||||
VAPID_PRIVATE_KEY=generated_vapid_private_key
|
VAPID_PRIVATE_KEY=generated_vapid_private_key
|
||||||
FIREBASE_CERT='{"type":"service_account",...}'
|
|
||||||
|
|
||||||
# Compliance (public key only - private key stays offline)
|
# Compliance (public key only - private key stays offline)
|
||||||
COMPLIANCE_PUBLIC_KEY=base64_encoded_public_key
|
COMPLIANCE_PUBLIC_KEY=base64_encoded_public_key
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The main backend **requires** Firebase for Android push (FCM). It is not generated into `.env`: `docker-compose.yml` sets `FIREBASE_CERT` and read-only-mounts `backend/firebase-cert.json` from the repo. Place your Firebase service account JSON at `backend/firebase-cert.json` before `docker compose up` (gitignored; excluded from the image build via the repo-root `.dockerignore`).
|
||||||
|
|
||||||
## Deployment Commands
|
## Deployment Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+302
-5
@@ -1,15 +1,312 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# _ENV_TEMPLATE: one KEY=value per line. Use <set> for stdin prompts. Use
|
||||||
|
# <gen:…> only where a dedicated step is needed. Any $(command) here runs when
|
||||||
|
# this script executes (after cd "$ROOT"). Piped stdin order: five <set> lines
|
||||||
|
# (TURN_USERNAME, TURN_SECRET, DEPLOYMENT_SERVER, FIREBASE_CERT, RELEASES_TOKEN),
|
||||||
|
# then commit (y/n), then deployment output directory (blank = deployment), then
|
||||||
|
# writes <dir>/.env and <dir>/compliance_keypair.txt (default dir: deployment); then
|
||||||
|
# if each target exists, backup prompt [Y/n] (Enter = yes; only n/no skips).
|
||||||
|
# Nothing is written until commit=y (including compliance_keypair.txt). Backups after commit=y, default yes.
|
||||||
|
# Backups use deployment/.env.backup.<6-char sha256 prefix>.bak (git-style); same
|
||||||
|
# contents reuse one file. If that name exists with different content, full hash is used.
|
||||||
|
# Each written file uses <path>.backup.<short>.bak beside the target (same hash rules).
|
||||||
|
# Template is read from fd 3 so stdin stays free.
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
echo > deployment/.env
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
./.venv/bin/python3 backend/services/main/generate_vapid_keys.py >> deployment/.env
|
VENV_PY="${ROOT}/.venv/bin/python3"
|
||||||
|
ENV_PATH=""
|
||||||
|
COMPLIANCE_TXT=""
|
||||||
|
_COMPLIANCE_PRIVATE_B64=""
|
||||||
|
_COMPLIANCE_PUBLIC_B64=""
|
||||||
|
|
||||||
cat >> deployment/.env <<EOF
|
_ENV_TEMPLATE="$(cat <<EOF
|
||||||
JWT_SECRET="$(openssl rand -base64 32)"
|
$(.venv/bin/python3 backend/services/main/generate_vapid_keys.py </dev/null)
|
||||||
COMPLIANCE_PUBLIC_KEY="$(./.venv/bin/python3 scripts/generate_compliance_keypair.py --save --public-only)"
|
JWT_SECRET=$(openssl rand -base64 32 </dev/null | tr -d '\n')
|
||||||
|
COMPLIANCE_PUBLIC_KEY=<gen:compliance>
|
||||||
TURN_USERNAME=<set>
|
TURN_USERNAME=<set>
|
||||||
TURN_SECRET=<set>
|
TURN_SECRET=<set>
|
||||||
DEPLOYMENT_SERVER=<set>
|
DEPLOYMENT_SERVER=<set>
|
||||||
FIREBASE_CERT=<set>
|
FIREBASE_CERT=<set>
|
||||||
|
POSTGRES_PASSWORD=$(openssl rand -hex 8 </dev/null)
|
||||||
|
MAIN_DB_PASSWORD=$(openssl rand -hex 8 </dev/null)
|
||||||
|
MESSAGING_DB_PASSWORD=$(openssl rand -hex 8 </dev/null)
|
||||||
|
FILE_STORAGE_DB_PASSWORD=$(openssl rand -hex 8 </dev/null)
|
||||||
RELEASES_TOKEN=<set>
|
RELEASES_TOKEN=<set>
|
||||||
|
MESSAGE_RETENTION_DAYS=180
|
||||||
EOF
|
EOF
|
||||||
|
)"
|
||||||
|
|
||||||
|
# --- colors (key = light blue, = gray, value = purple) ---
|
||||||
|
NC=$'\033[0m'
|
||||||
|
GRAY=$'\033[38;5;245m'
|
||||||
|
BLUE=$'\033[38;5;81m'
|
||||||
|
PURPLE=$'\033[38;5;141m'
|
||||||
|
RED=$'\033[38;5;203m'
|
||||||
|
LIME=$'\033[38;5;154m'
|
||||||
|
ORANGE=$'\033[38;5;208m'
|
||||||
|
YELLOW=$'\033[38;5;226m'
|
||||||
|
CHECK=$'\033[38;5;154m'
|
||||||
|
WARN_ICON=$'\xe2\x9a\xa0'
|
||||||
|
|
||||||
|
_abort_on_int() {
|
||||||
|
printf '\n\n%b%s %s%b\n' "$YELLOW" "$WARN_ICON" "Aborted." "$NC" >&2
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
trap _abort_on_int INT
|
||||||
|
|
||||||
|
# Buffered .env lines (written only after commit)
|
||||||
|
declare -a ENV_LINES=()
|
||||||
|
|
||||||
|
# label + label_color | KEY=value (KEY light blue, = gray, value purple)
|
||||||
|
print_kv_row() {
|
||||||
|
local label="$1" label_c="$2" key="$3" val="$4"
|
||||||
|
printf '%b%s%b %b|%b %b%s%b%b=%b%s%b\n' \
|
||||||
|
"$label_c" "$label" "$NC" "$GRAY" "$NC" \
|
||||||
|
"$BLUE" "$key" "$NC" "$GRAY" "$PURPLE" "$val" "$NC"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_validation_error() {
|
||||||
|
printf '%b%s %s%b\n' "$RED" "$WARN_ICON" "$1" "$NC" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Append one logical line to ENV_LINES (shell-safe quoting for .env file)
|
||||||
|
buffer_env_line() {
|
||||||
|
local key="$1" val="$2"
|
||||||
|
local line
|
||||||
|
if [[ "$val" == *'"'* ]] || [[ "$val" == *' '* ]] || [[ "$val" == *'#'* ]] || [[ "$val" == *'='* ]] || [[ -z "$val" ]]; then
|
||||||
|
local esc="${val//\\/\\\\}"
|
||||||
|
esc="${esc//\"/\\\"}"
|
||||||
|
line=$(printf '%s="%s"' "$key" "$esc")
|
||||||
|
else
|
||||||
|
line=$(printf '%s=%s' "$key" "$val")
|
||||||
|
fi
|
||||||
|
ENV_LINES+=("$line")
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_ipv4() {
|
||||||
|
local ip="$1" _IFS=$IFS IFS=.
|
||||||
|
local -a oct=($ip)
|
||||||
|
IFS="$_IFS"
|
||||||
|
[[ ${#oct[@]} -eq 4 ]] || return 1
|
||||||
|
local x
|
||||||
|
for x in "${oct[@]}"; do
|
||||||
|
[[ "$x" =~ ^[0-9]+$ ]] || return 1
|
||||||
|
(( 10#$x >= 0 && 10#$x <= 255 )) || return 1
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_deployment_server() {
|
||||||
|
local v="$1"
|
||||||
|
[[ -n "$v" ]] || return 1
|
||||||
|
validate_ipv4 "$v"
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_set_value() {
|
||||||
|
local key="$1" val="$2"
|
||||||
|
[[ -n "$val" ]] || return 1
|
||||||
|
case "$key" in
|
||||||
|
DEPLOYMENT_SERVER) validate_deployment_server "$val" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
validation_hint() {
|
||||||
|
case "$1" in
|
||||||
|
DEPLOYMENT_SERVER)
|
||||||
|
printf '%s' "Expected a valid IPv4 address (e.g. 192.168.1.1), four octets 0–255."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf '%s' "Value must not be empty."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_set() {
|
||||||
|
local key="$1"
|
||||||
|
local val=""
|
||||||
|
while true; do
|
||||||
|
printf '%b%s%b %b|%b %b%s%b%b=%b' \
|
||||||
|
"$ORANGE" "user input" "$NC" "$GRAY" "$NC" "$BLUE" "$key" "$NC" "$GRAY" "$NC" >&2
|
||||||
|
IFS= read -r val || true
|
||||||
|
if validate_set_value "$key" "$val"; then
|
||||||
|
buffer_env_line "$key" "$val"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
print_validation_error "$(validation_hint "$key")"
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
printf '%s\n' "generate:env: invalid value for ${key} (piped stdin); aborting." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# $2 = backup path prefix without .<hash>.bak (use "${src}.backup")
|
||||||
|
_do_backup_copy() {
|
||||||
|
local src="$1"
|
||||||
|
local dest_prefix="$2"
|
||||||
|
local full short dest
|
||||||
|
full="$(openssl dgst -sha256 -r <"$src" | awk '{print $1}')"
|
||||||
|
short="${full:0:6}"
|
||||||
|
dest="${dest_prefix}.${short}.bak"
|
||||||
|
if [[ -f "$dest" ]]; then
|
||||||
|
if cmp -s "$src" "$dest"; then
|
||||||
|
print_kv_row "backup" "$GRAY" "backup_unchanged" "$dest"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
dest="${dest_prefix}.${full}.bak"
|
||||||
|
if [[ -f "$dest" ]] && cmp -s "$src" "$dest"; then
|
||||||
|
print_kv_row "backup" "$GRAY" "backup_unchanged" "$dest"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cp "$src" "$dest"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_gen_compliance() {
|
||||||
|
local tmp
|
||||||
|
tmp="$(mktemp "${TMPDIR:-/tmp}/fromchat-compliance.XXXXXX")"
|
||||||
|
"$VENV_PY" scripts/generate_compliance_keypair.py --emit-key-lines </dev/null >"$tmp"
|
||||||
|
{
|
||||||
|
IFS= read -r _COMPLIANCE_PRIVATE_B64
|
||||||
|
IFS= read -r _COMPLIANCE_PUBLIC_B64
|
||||||
|
} <"$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
if [[ -z "$_COMPLIANCE_PRIVATE_B64" || -z "$_COMPLIANCE_PUBLIC_B64" ]]; then
|
||||||
|
echo "generate:env: compliance keypair generation failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
print_kv_row "generated " "$LIME" "COMPLIANCE_PUBLIC_KEY" "$_COMPLIANCE_PUBLIC_B64"
|
||||||
|
buffer_env_line "COMPLIANCE_PUBLIC_KEY" "$_COMPLIANCE_PUBLIC_B64"
|
||||||
|
}
|
||||||
|
|
||||||
|
_write_compliance_keypair_txt() {
|
||||||
|
[[ -n "$_COMPLIANCE_PRIVATE_B64" && -n "$_COMPLIANCE_PUBLIC_B64" ]] || return 0
|
||||||
|
[[ -n "$COMPLIANCE_TXT" ]] || return 0
|
||||||
|
mkdir -p "$(dirname "$COMPLIANCE_TXT")"
|
||||||
|
local ts
|
||||||
|
ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||||
|
cat >"$COMPLIANCE_TXT" <<EOF
|
||||||
|
COMPLIANCE SYSTEM X25519 KEYPAIR
|
||||||
|
Generated: ${ts}
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
PRIVATE KEY (STORE OFFLINE ON AIR-GAPPED MACHINE):
|
||||||
|
${_COMPLIANCE_PRIVATE_B64}
|
||||||
|
|
||||||
|
PUBLIC KEY (SET AS COMPLIANCE_PUBLIC_KEY ENV VAR):
|
||||||
|
${_COMPLIANCE_PUBLIC_B64}
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
⚠️ SECURITY WARNING:
|
||||||
|
- Keep the PRIVATE KEY offline on an air-gapped machine
|
||||||
|
- Only the PUBLIC KEY should be deployed to servers
|
||||||
|
- Never commit private key to version control
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
process_line() {
|
||||||
|
local line="$1"
|
||||||
|
[[ -z "$line" ]] && return 0
|
||||||
|
[[ "$line" =~ ^[[:space:]]*# ]] && return 0
|
||||||
|
local key rhs
|
||||||
|
key="${line%%=*}"
|
||||||
|
rhs="${line#*=}"
|
||||||
|
key="${key%"${key##*[![:space:]]}"}"
|
||||||
|
key="${key#"${key%%[![:space:]]*}"}"
|
||||||
|
|
||||||
|
case "$rhs" in
|
||||||
|
\<set\>)
|
||||||
|
prompt_set "$key"
|
||||||
|
;;
|
||||||
|
\<gen:compliance\>)
|
||||||
|
run_gen_compliance
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ "$rhs" == \<gen:* ]]; then
|
||||||
|
echo "Unknown template token for ${key}=${rhs}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
print_kv_row "generated " "$LIME" "$key" "$rhs"
|
||||||
|
buffer_env_line "$key" "$rhs"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
read_yes() {
|
||||||
|
local prompt="$1"
|
||||||
|
local a
|
||||||
|
printf '%b%s%b' "$GRAY" "$prompt" "$NC" >&2
|
||||||
|
IFS= read -r a || true
|
||||||
|
[[ "${a:-}" =~ ^[yY]([eE][sS])?$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Backups: safe default yes — only explicit n/no skips; Enter, y/yes, or anything else → backup
|
||||||
|
read_yes_default_yes() {
|
||||||
|
local prompt="$1" a
|
||||||
|
printf '%b%s%b' "$GRAY" "$prompt" "$NC" >&2
|
||||||
|
IFS= read -r a || true
|
||||||
|
a="${a#"${a%%[![:space:]]*}"}"
|
||||||
|
a="${a%"${a##*[![:space:]]}"}"
|
||||||
|
[[ "$a" =~ ^[nN]([oO])?$ ]] && return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sets global named by $1 to trimmed read line or default $2; $3 = stderr label.
|
||||||
|
prompt_output_path() {
|
||||||
|
local _out_var="$1" _default="$2" _label="$3" _line
|
||||||
|
printf '%b%s%b ' "$GRAY" "$_label" "$NC" >&2
|
||||||
|
printf '[%s]: ' "$_default" >&2
|
||||||
|
IFS= read -r _line || true
|
||||||
|
_line="${_line#"${_line%%[![:space:]]*}"}"
|
||||||
|
_line="${_line%"${_line##*[![:space:]]}"}"
|
||||||
|
if [[ -z "$_line" ]]; then
|
||||||
|
printf -v "$_out_var" '%s' "$_default"
|
||||||
|
else
|
||||||
|
printf -v "$_out_var" '%s' "$_line"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- main: build buffer only ---
|
||||||
|
exec 3<<< "$_ENV_TEMPLATE"
|
||||||
|
while IFS= read -r line <&3 || [[ -n "$line" ]]; do
|
||||||
|
process_line "$line"
|
||||||
|
done
|
||||||
|
exec 3<&-
|
||||||
|
|
||||||
|
printf '\n' >&2
|
||||||
|
if ! read_yes "Write generated files? [y/N]: "; then
|
||||||
|
printf '%bAborted (no commit).%b\n' "$RED" "$NC" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
prompt_output_path DEPLOY_OUTPUT_DIR "deployment" "Deployment output directory (under repo)"
|
||||||
|
DEPLOY_OUTPUT_DIR="${DEPLOY_OUTPUT_DIR%/}"
|
||||||
|
if [[ "$DEPLOY_OUTPUT_DIR" != /* ]]; then
|
||||||
|
DEPLOY_OUTPUT_DIR="${ROOT}/${DEPLOY_OUTPUT_DIR}"
|
||||||
|
fi
|
||||||
|
ENV_PATH="${DEPLOY_OUTPUT_DIR}/.env"
|
||||||
|
COMPLIANCE_TXT="${DEPLOY_OUTPUT_DIR}/compliance_keypair.txt"
|
||||||
|
|
||||||
|
if [[ -f "$ENV_PATH" ]] && read_yes_default_yes "File exists: ${ENV_PATH}. Create backup before overwrite? [Y/n]: "; then
|
||||||
|
_do_backup_copy "$ENV_PATH" "${ENV_PATH}.backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$COMPLIANCE_TXT" ]] && read_yes_default_yes "File exists: ${COMPLIANCE_TXT}. Create backup before overwrite? [Y/n]: "; then
|
||||||
|
_do_backup_copy "$COMPLIANCE_TXT" "${COMPLIANCE_TXT}.backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$ENV_PATH")"
|
||||||
|
printf '%s\n' "${ENV_LINES[@]}" >"$ENV_PATH"
|
||||||
|
_write_compliance_keypair_txt
|
||||||
|
|
||||||
|
printf '\n%b✓ env written to %s%b\n' "$CHECK" "$ENV_PATH" "$NC"
|
||||||
|
if [[ -n "$_COMPLIANCE_PUBLIC_B64" ]]; then
|
||||||
|
printf '%b✓ compliance keypair written to %s%b\n' "$CHECK" "$COMPLIANCE_TXT" "$NC"
|
||||||
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user