initial commit
This commit is contained in:
Executable
+160
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# SHARP-ER - check that this machine can run the project
|
||||
# ----------------------------------------------------------------------------
|
||||
# ./scripts/check-prereqs.sh check both setup paths, report
|
||||
# ./scripts/check-prereqs.sh manual only the tools PATH A needs
|
||||
# ./scripts/check-prereqs.sh docker only the tools PATH B needs
|
||||
#
|
||||
# Exits non-zero when a tool the chosen path REQUIRES is missing or too old, so
|
||||
# it can be used as a gate in CI or in an onboarding script. Read-only: it
|
||||
# starts nothing, installs nothing and writes nothing.
|
||||
# ============================================================================
|
||||
set -uo pipefail
|
||||
|
||||
MODE="${1:-all}"
|
||||
case "$MODE" in
|
||||
all|manual|docker) ;;
|
||||
-h|--help)
|
||||
printf 'usage: check-prereqs.sh [all|manual|docker]\n'
|
||||
exit 0 ;;
|
||||
*)
|
||||
printf 'unknown mode: %s (use all, manual or docker)\n' "$MODE" >&2
|
||||
exit 2 ;;
|
||||
esac
|
||||
|
||||
# Versions this repository is developed against.
|
||||
NODE_MIN="22.22.0" # React Router 8 requires >= 22.22.0
|
||||
PNPM_MAJOR="9" # packageManager: pnpm@9.12.3
|
||||
PG_MAJOR="16"
|
||||
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
||||
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
|
||||
else
|
||||
C_OK=""; C_WARN=""; C_ERR=""; C_DIM=""; C_OFF=""
|
||||
fi
|
||||
|
||||
FAILED=0
|
||||
pass() { printf '%s ok %s %-22s %s\n' "$C_OK" "$C_OFF" "$1" "${2:-}"; }
|
||||
warn() { printf '%s warn %s %-22s %s\n' "$C_WARN" "$C_OFF" "$1" "${2:-}"; }
|
||||
fail() { printf '%s fail %s %-22s %s\n' "$C_ERR" "$C_OFF" "$1" "${2:-}"; FAILED=1; }
|
||||
note() { printf '%s %s %-22s %s\n' "$C_DIM" "$C_OFF" "" "$1"; }
|
||||
|
||||
# true when $1 >= $2, comparing dotted versions numerically.
|
||||
version_ge() {
|
||||
[ "$1" = "$2" ] && return 0
|
||||
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
|
||||
}
|
||||
|
||||
# Strip a leading v and anything after the numeric part: "v22.23.2" -> 22.23.2
|
||||
clean_version() { printf '%s' "$1" | sed -E 's/^v//; s/[^0-9.].*$//'; }
|
||||
|
||||
check_node() {
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
fail "node" "not installed - need >= $NODE_MIN"
|
||||
note "nvm install \$(cat .nvmrc) # .nvmrc pins the version this repo uses"
|
||||
return
|
||||
fi
|
||||
local version; version="$(clean_version "$(node --version)")"
|
||||
if version_ge "$version" "$NODE_MIN"; then
|
||||
pass "node $version" ">= $NODE_MIN"
|
||||
else
|
||||
fail "node $version" "too old - React Router 8 requires >= $NODE_MIN"
|
||||
note "nvm install \$(cat .nvmrc) && nvm use"
|
||||
fi
|
||||
}
|
||||
|
||||
check_pnpm() {
|
||||
if ! command -v pnpm >/dev/null 2>&1; then
|
||||
fail "pnpm" "not installed - need $PNPM_MAJOR.x"
|
||||
note "corepack enable && corepack prepare pnpm@9.12.3 --activate"
|
||||
return
|
||||
fi
|
||||
local version major; version="$(clean_version "$(pnpm --version)")"; major="${version%%.*}"
|
||||
if [ "$major" = "$PNPM_MAJOR" ]; then
|
||||
pass "pnpm $version" "major $PNPM_MAJOR"
|
||||
else
|
||||
warn "pnpm $version" "repo pins pnpm@9.12.3 - the lockfile may be rewritten"
|
||||
fi
|
||||
}
|
||||
|
||||
check_postgres_client() {
|
||||
if ! command -v psql >/dev/null 2>&1; then
|
||||
fail "psql" "not installed - need the PostgreSQL $PG_MAJOR client"
|
||||
note "sudo apt install postgresql-$PG_MAJOR postgresql-client-$PG_MAJOR"
|
||||
return
|
||||
fi
|
||||
local version major
|
||||
version="$(clean_version "$(psql --version | awk '{print $3}')")"
|
||||
major="${version%%.*}"
|
||||
if [ "$major" = "$PG_MAJOR" ]; then
|
||||
pass "psql $version" "major $PG_MAJOR"
|
||||
else
|
||||
warn "psql $version" "developed against PostgreSQL $PG_MAJOR"
|
||||
fi
|
||||
}
|
||||
|
||||
check_postgres_server() {
|
||||
command -v pg_isready >/dev/null 2>&1 || { note "pg_isready not found - skipped the server check"; return; }
|
||||
local host="${DATABASE_HOST:-127.0.0.1}" port="${DATABASE_PORT:-5432}"
|
||||
if pg_isready --host "$host" --port "$port" --timeout 3 >/dev/null 2>&1; then
|
||||
pass "postgres server" "answering on $host:$port"
|
||||
else
|
||||
warn "postgres server" "nothing on $host:$port - start it before ./scripts/setup-db.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
check_docker() {
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
fail "docker" "not installed"
|
||||
note "https://docs.docker.com/engine/install/"
|
||||
return
|
||||
fi
|
||||
pass "docker" "$(docker --version 2>/dev/null | sed 's/,.*//')"
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
pass "docker compose" "$(docker compose version --short 2>/dev/null)"
|
||||
else
|
||||
fail "docker compose" "v2 plugin missing (the old docker-compose binary is not supported)"
|
||||
fi
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
warn "docker daemon" "not reachable - start Docker, or add your user to the docker group"
|
||||
fi
|
||||
}
|
||||
|
||||
check_env_files() {
|
||||
local root; root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
[ -f "$root/.env" ] && pass ".env" "present" || warn ".env" "missing - cp .env.example .env"
|
||||
if [ "$MODE" != "docker" ]; then
|
||||
[ -f "$root/apps/cms/.env" ] \
|
||||
&& pass "apps/cms/.env" "present" \
|
||||
|| warn "apps/cms/.env" "missing - cp apps/cms/.env.example apps/cms/.env"
|
||||
fi
|
||||
}
|
||||
|
||||
printf '\nSHARP-ER prerequisites (%s)\n\n' "$MODE"
|
||||
|
||||
if [ "$MODE" = "manual" ] || [ "$MODE" = "all" ]; then
|
||||
printf '%sPATH A - manual%s\n' "$C_DIM" "$C_OFF"
|
||||
check_node
|
||||
check_pnpm
|
||||
check_postgres_client
|
||||
check_postgres_server
|
||||
printf '\n'
|
||||
fi
|
||||
|
||||
if [ "$MODE" = "docker" ] || [ "$MODE" = "all" ]; then
|
||||
printf '%sPATH B - docker%s\n' "$C_DIM" "$C_OFF"
|
||||
check_docker
|
||||
printf '\n'
|
||||
fi
|
||||
|
||||
printf '%sEnvironment files%s\n' "$C_DIM" "$C_OFF"
|
||||
check_env_files
|
||||
printf '\n'
|
||||
|
||||
if [ "$FAILED" -ne 0 ]; then
|
||||
printf '%sSomething required is missing - see the lines marked fail.%s\n\n' "$C_ERR" "$C_OFF"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%sReady. Next: README.md, "PATH A" or "PATH B".%s\n\n' "$C_OK" "$C_OFF"
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# SHARP-ER - generate the Strapi secrets an environment file needs
|
||||
# ----------------------------------------------------------------------------
|
||||
# ./scripts/generate-secrets.sh print a block to paste
|
||||
# ./scripts/generate-secrets.sh --write .env fill the empty keys in .env
|
||||
# ./scripts/generate-secrets.sh --write apps/cms/.env
|
||||
# ./scripts/generate-secrets.sh --write .env --force rotate values already set
|
||||
#
|
||||
# Safe by default: an existing, non-placeholder value is never overwritten
|
||||
# unless --force is given, so re-running it after adding one key only fills the
|
||||
# gap. With --force the previous file is kept as <file>.bak.
|
||||
#
|
||||
# Rotating APP_KEYS signs everyone out of the admin panel; rotating
|
||||
# ENCRYPTION_KEY makes values already encrypted in the database unreadable.
|
||||
# ============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
KEYS=(APP_KEYS API_TOKEN_SALT ADMIN_JWT_SECRET JWT_SECRET TRANSFER_TOKEN_SALT ENCRYPTION_KEY)
|
||||
APP_KEYS_COUNT=4
|
||||
|
||||
TARGET=""
|
||||
FORCE=0
|
||||
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
||||
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
|
||||
else
|
||||
C_OK=""; C_WARN=""; C_ERR=""; C_DIM=""; C_OFF=""
|
||||
fi
|
||||
ok() { printf '%s ok %s %s\n' "$C_OK" "$C_OFF" "$*"; }
|
||||
info() { printf '%s .. %s %s\n' "$C_DIM" "$C_OFF" "$*"; }
|
||||
warn() { printf '%s warn %s %s\n' "$C_WARN" "$C_OFF" "$*" >&2; }
|
||||
die() { printf '%s fail %s %s\n' "$C_ERR" "$C_OFF" "$*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
generate-secrets.sh - fresh random secrets for Strapi.
|
||||
|
||||
./scripts/generate-secrets.sh print a block to paste
|
||||
./scripts/generate-secrets.sh --write .env fill empty keys in place
|
||||
./scripts/generate-secrets.sh --write .env --force rotate values already set
|
||||
|
||||
Keys handled: APP_KEYS (4 comma-separated), API_TOKEN_SALT, ADMIN_JWT_SECRET,
|
||||
JWT_SECRET, TRANSFER_TOKEN_SALT, ENCRYPTION_KEY.
|
||||
|
||||
If the target does not exist but a matching .env.example sits next to it, the
|
||||
template is copied first, so this also bootstraps a new environment file.
|
||||
Existing values are preserved unless --force is passed (previous file kept as
|
||||
<file>.bak).
|
||||
USAGE
|
||||
exit 0
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--write) TARGET="${2:?--write needs a file path}"; shift 2 ;;
|
||||
--force) FORCE=1; shift ;;
|
||||
-h|--help) usage ;;
|
||||
*) die "unknown option: $1 (try --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Randomness -------------------------------------------------------------
|
||||
# 32 bytes, base64. openssl is used when present; node and /dev/urandom are
|
||||
# equally good fallbacks so the script works on a bare machine.
|
||||
random_secret() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -base64 32 | tr -d '\n'
|
||||
elif command -v node >/dev/null 2>&1; then
|
||||
node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('base64'))"
|
||||
elif [ -r /dev/urandom ]; then
|
||||
head -c 32 /dev/urandom | base64 | tr -d '\n'
|
||||
else
|
||||
die "no source of randomness found (need openssl, node, or /dev/urandom)."
|
||||
fi
|
||||
}
|
||||
|
||||
value_for() {
|
||||
local key="$1" i out=""
|
||||
if [ "$key" = "APP_KEYS" ]; then
|
||||
for ((i = 0; i < APP_KEYS_COUNT; i++)); do
|
||||
out+="${out:+,}$(random_secret)"
|
||||
done
|
||||
printf '%s' "$out"
|
||||
else
|
||||
random_secret
|
||||
fi
|
||||
}
|
||||
|
||||
# A value that still has to be replaced: empty, or one of the template's
|
||||
# own placeholders.
|
||||
is_placeholder() {
|
||||
local value="$1"
|
||||
value="${value%\"}"; value="${value#\"}"
|
||||
value="${value%\'}"; value="${value#\'}"
|
||||
[ -z "$value" ] && return 0
|
||||
case "$value" in
|
||||
replace-with*|changeme*|CHANGEME*|todo*|TODO*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- Print mode -------------------------------------------------------------
|
||||
if [ -z "$TARGET" ]; then
|
||||
printf '# Strapi secrets - generated %s. Paste into .env / apps/cms/.env.\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
for key in "${KEYS[@]}"; do
|
||||
printf '%s=%s\n' "$key" "$(value_for "$key")"
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Write mode -------------------------------------------------------------
|
||||
if [ ! -f "$TARGET" ]; then
|
||||
template="$(dirname -- "$TARGET")/.env.example"
|
||||
[ -f "$template" ] || die "$TARGET does not exist and there is no $template to copy."
|
||||
cp -- "$template" "$TARGET"
|
||||
info "created $TARGET from $(basename -- "$(dirname -- "$template")")/.env.example"
|
||||
fi
|
||||
|
||||
tmp="$(mktemp "${TARGET}.XXXXXX")"
|
||||
# Secrets must never be world-readable, not even for a moment.
|
||||
chmod 600 "$tmp"
|
||||
trap 'rm -f -- "$tmp"' EXIT
|
||||
|
||||
declare -A seen=()
|
||||
filled=(); kept=(); rotated=()
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
key="${line%%=*}"
|
||||
handled=0
|
||||
for candidate in "${KEYS[@]}"; do
|
||||
[ "$key" = "$candidate" ] || continue
|
||||
handled=1
|
||||
seen["$candidate"]=1
|
||||
current="${line#*=}"
|
||||
if [ "$FORCE" -eq 1 ]; then
|
||||
printf '%s=%s\n' "$candidate" "$(value_for "$candidate")" >> "$tmp"
|
||||
rotated+=("$candidate")
|
||||
elif is_placeholder "$current"; then
|
||||
printf '%s=%s\n' "$candidate" "$(value_for "$candidate")" >> "$tmp"
|
||||
filled+=("$candidate")
|
||||
else
|
||||
printf '%s\n' "$line" >> "$tmp"
|
||||
kept+=("$candidate")
|
||||
fi
|
||||
break
|
||||
done
|
||||
[ "$handled" -eq 1 ] || printf '%s\n' "$line" >> "$tmp"
|
||||
done < "$TARGET"
|
||||
|
||||
missing=()
|
||||
for key in "${KEYS[@]}"; do
|
||||
[ -n "${seen[$key]:-}" ] || missing+=("$key")
|
||||
done
|
||||
|
||||
if [ "${#missing[@]}" -gt 0 ]; then
|
||||
{
|
||||
printf '\n# Strapi secrets - added by scripts/generate-secrets.sh on %s\n' "$(date -u '+%Y-%m-%d')"
|
||||
for key in "${missing[@]}"; do
|
||||
printf '%s=%s\n' "$key" "$(value_for "$key")"
|
||||
done
|
||||
} >> "$tmp"
|
||||
filled+=("${missing[@]}")
|
||||
fi
|
||||
|
||||
if [ "$FORCE" -eq 1 ]; then
|
||||
cp -- "$TARGET" "${TARGET}.bak"
|
||||
chmod 600 "${TARGET}.bak"
|
||||
warn "previous values kept in ${TARGET}.bak - delete it once the new ones work."
|
||||
fi
|
||||
|
||||
cat -- "$tmp" > "$TARGET"
|
||||
chmod 600 "$TARGET"
|
||||
rm -f -- "$tmp"
|
||||
trap - EXIT
|
||||
|
||||
[ "${#filled[@]}" -gt 0 ] && ok "generated: ${filled[*]}"
|
||||
[ "${#rotated[@]}" -gt 0 ] && ok "rotated: ${rotated[*]}"
|
||||
[ "${#kept[@]}" -gt 0 ] && info "left alone (already set): ${kept[*]}"
|
||||
ok "$TARGET is ready (mode 600)"
|
||||
exit 0
|
||||
Executable
+168
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# SHARP-ER - create and verify the CMS database (manual setup, PATH A)
|
||||
# ----------------------------------------------------------------------------
|
||||
# Idempotent: running it twice is a no-op. It never drops anything and never
|
||||
# touches data - it only creates the database if it is missing, then proves it
|
||||
# can be connected to with the credentials Strapi will use.
|
||||
#
|
||||
# ./scripts/setup-db.sh # use .env / the defaults below
|
||||
# ./scripts/setup-db.sh --name other_db # override one value
|
||||
# ./scripts/setup-db.sh --help
|
||||
#
|
||||
# Docker users do not need this script: the postgres service creates the
|
||||
# database from POSTGRES_DB on first boot.
|
||||
# ============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
# --- Defaults, overridden by .env, overridden by flags ----------------------
|
||||
DB_NAME="sharper_cms"
|
||||
DB_USER="postgres"
|
||||
DB_PASSWORD=""
|
||||
DB_HOST="127.0.0.1"
|
||||
DB_PORT="5432"
|
||||
|
||||
# --- Output helpers --------------------------------------------------------
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
||||
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
|
||||
else
|
||||
C_OK=""; C_WARN=""; C_ERR=""; C_DIM=""; C_OFF=""
|
||||
fi
|
||||
ok() { printf '%s ok %s %s\n' "$C_OK" "$C_OFF" "$*"; }
|
||||
info() { printf '%s .. %s %s\n' "$C_DIM" "$C_OFF" "$*"; }
|
||||
warn() { printf '%s warn %s %s\n' "$C_WARN" "$C_OFF" "$*" >&2; }
|
||||
die() { printf '%s fail %s %s\n' "$C_ERR" "$C_OFF" "$*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
setup-db.sh - create and verify the SHARP-ER CMS database (manual setup).
|
||||
|
||||
Idempotent: it never drops anything and never touches data. It creates the
|
||||
database only if it is missing, then proves it can be connected to with the
|
||||
credentials Strapi will use.
|
||||
|
||||
./scripts/setup-db.sh use .env / the defaults
|
||||
./scripts/setup-db.sh --name other_db override one value
|
||||
./scripts/setup-db.sh --host db.internal --port 5433
|
||||
|
||||
Options:
|
||||
--name database name (default sharper_cms)
|
||||
--user database role (default postgres)
|
||||
--password role password (default: DATABASE_PASSWORD from .env)
|
||||
--host server host (default 127.0.0.1)
|
||||
--port server port (default 5432)
|
||||
-h, --help show this and exit
|
||||
|
||||
Values come from ./.env, then apps/cms/.env, then these flags - last wins.
|
||||
Docker users do not need this script: the postgres service creates the
|
||||
database from POSTGRES_DB on first boot.
|
||||
USAGE
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --help must work before anything else touches the filesystem or the server.
|
||||
for arg in "$@"; do
|
||||
case "$arg" in -h|--help) usage ;; esac
|
||||
done
|
||||
|
||||
# --- .env (root) ------------------------------------------------------------
|
||||
# Read DATABASE_* only, so a stray line in .env cannot redefine anything else.
|
||||
read_env_file() {
|
||||
local file="$1" line key value
|
||||
[ -f "$file" ] || return 0
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
case "$line" in
|
||||
DATABASE_NAME=*|DATABASE_USERNAME=*|DATABASE_PASSWORD=*|DATABASE_HOST=*|DATABASE_PORT=*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
value="${value%\"}"; value="${value#\"}"
|
||||
value="${value%\'}"; value="${value#\'}"
|
||||
[ -n "$value" ] || continue
|
||||
case "$key" in
|
||||
DATABASE_NAME) DB_NAME="$value" ;;
|
||||
DATABASE_USERNAME) DB_USER="$value" ;;
|
||||
DATABASE_PASSWORD) DB_PASSWORD="$value" ;;
|
||||
DATABASE_HOST) DB_HOST="$value" ;;
|
||||
DATABASE_PORT) DB_PORT="$value" ;;
|
||||
esac
|
||||
done < "$file"
|
||||
info "read defaults from ${file#"$REPO_ROOT"/}"
|
||||
}
|
||||
|
||||
read_env_file "$REPO_ROOT/.env"
|
||||
read_env_file "$REPO_ROOT/apps/cms/.env"
|
||||
|
||||
# --- Flags ------------------------------------------------------------------
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--name) DB_NAME="${2:?--name needs a value}"; shift 2 ;;
|
||||
--user) DB_USER="${2:?--user needs a value}"; shift 2 ;;
|
||||
--password) DB_PASSWORD="${2:?--password needs a value}"; shift 2 ;;
|
||||
--host) DB_HOST="${2:?--host needs a value}"; shift 2 ;;
|
||||
--port) DB_PORT="${2:?--port needs a value}"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) die "unknown option: $1 (try --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$DB_PASSWORD" ] && export PGPASSWORD="$DB_PASSWORD"
|
||||
|
||||
PSQL=(psql --host "$DB_HOST" --port "$DB_PORT" --username "$DB_USER" --no-password --quiet --tuples-only --no-align)
|
||||
|
||||
# --- 1. Client tools --------------------------------------------------------
|
||||
command -v psql >/dev/null 2>&1 || die "psql not found. Install the PostgreSQL 16 client (Debian/Ubuntu: sudo apt install postgresql-client-16)."
|
||||
command -v createdb >/dev/null 2>&1 || die "createdb not found. It ships with the PostgreSQL client package."
|
||||
ok "client tools found ($(psql --version))"
|
||||
|
||||
# --- 2. Server reachable ----------------------------------------------------
|
||||
if command -v pg_isready >/dev/null 2>&1; then
|
||||
pg_isready --host "$DB_HOST" --port "$DB_PORT" --timeout 5 >/dev/null 2>&1 \
|
||||
|| die "no PostgreSQL server answering on $DB_HOST:$DB_PORT. Start it (sudo systemctl start postgresql) or point --host/--port somewhere else."
|
||||
fi
|
||||
if ! "${PSQL[@]}" --dbname postgres --command "SELECT 1" >/dev/null 2>&1; then
|
||||
die "cannot authenticate as \"$DB_USER\" on $DB_HOST:$DB_PORT. Check DATABASE_USERNAME / DATABASE_PASSWORD in .env, or pass --user / --password."
|
||||
fi
|
||||
ok "connected to $DB_HOST:$DB_PORT as \"$DB_USER\""
|
||||
|
||||
# --- 3. Database ------------------------------------------------------------
|
||||
db_exists() {
|
||||
local found
|
||||
found="$("${PSQL[@]}" --dbname postgres --command \
|
||||
"SELECT 1 FROM pg_database WHERE datname = '${DB_NAME//\'/\'\'}'" 2>/dev/null || true)"
|
||||
[ "$(printf '%s' "$found" | tr -d '[:space:]')" = "1" ]
|
||||
}
|
||||
|
||||
if db_exists; then
|
||||
ok "database \"$DB_NAME\" already exists - nothing to create"
|
||||
else
|
||||
info "creating database \"$DB_NAME\" ..."
|
||||
createdb --host "$DB_HOST" --port "$DB_PORT" --username "$DB_USER" --no-password \
|
||||
--encoding UTF8 --owner "$DB_USER" "$DB_NAME" \
|
||||
|| die "createdb failed. Does \"$DB_USER\" have CREATEDB rights?"
|
||||
ok "database \"$DB_NAME\" created (UTF8, owner \"$DB_USER\")"
|
||||
fi
|
||||
|
||||
# --- 4. Verify --------------------------------------------------------------
|
||||
SERVER_VERSION="$("${PSQL[@]}" --dbname "$DB_NAME" --command "SHOW server_version" | awk 'NR==1 {print $1}')"
|
||||
ENCODING="$("${PSQL[@]}" --dbname "$DB_NAME" --command \
|
||||
"SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()" | tr -d '[:space:]')"
|
||||
CAN_CREATE="$("${PSQL[@]}" --dbname "$DB_NAME" --command \
|
||||
"SELECT has_database_privilege(current_user, current_database(), 'CREATE')" | tr -d '[:space:]')"
|
||||
|
||||
[ "$CAN_CREATE" = "t" ] || warn "\"$DB_USER\" cannot CREATE in \"$DB_NAME\"; Strapi will not be able to build its tables."
|
||||
case "$SERVER_VERSION" in
|
||||
16*|17*|18*) ;;
|
||||
*) warn "server is PostgreSQL $SERVER_VERSION; this project is developed against 16." ;;
|
||||
esac
|
||||
|
||||
ok "verified: $DB_NAME on PostgreSQL $SERVER_VERSION, encoding $ENCODING"
|
||||
printf '\n'
|
||||
printf 'Put these in apps/cms/.env (cp apps/cms/.env.example apps/cms/.env first):\n'
|
||||
printf ' DATABASE_CLIENT=postgres\n'
|
||||
printf ' DATABASE_HOST=%s\n DATABASE_PORT=%s\n DATABASE_NAME=%s\n DATABASE_USERNAME=%s\n DATABASE_PASSWORD=%s\n' \
|
||||
"$DB_HOST" "$DB_PORT" "$DB_NAME" "$DB_USER" "${DB_PASSWORD:-<your password>}"
|
||||
printf '\nNext: ./scripts/generate-secrets.sh --write apps/cms/.env && pnpm dev\n'
|
||||
Reference in New Issue
Block a user