Ctrl K
ComunidadGuías…

install.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/bash
set -euo pipefail

# .env is mode 600. The secrets directory is mode 755 and the files are mode 644.
# compose mounts the whole directory at /run/secrets, so the container user (uid 33)
# must be able to traverse it. A mode 700 directory would hide the files from that user.
umask 077

# Set FILEDPAPERS_VERSION to a released tag to pin the downloaded compose.yaml, for example
#   FILEDPAPERS_VERSION=6.0.18 ./install.sh
if [ -n "${FILEDPAPERS_VERSION:-}" ]; then
  FILEDPAPERS_REF="refs/tags/${FILEDPAPERS_VERSION}"
else
  FILEDPAPERS_REF="refs/heads/main"
fi

BASE_URL="https://raw.githubusercontent.com/svenkubiak/filedpapers/${FILEDPAPERS_REF}"
COMPOSE_URL="${BASE_URL}/compose.yaml"
SECRETS_DIR="secrets"

# The file name is the configuration key. Mangoo reads file{} from /run/secrets/<key>.
# smtp.password is handled separately: it is the operator's mailbox password, not a generated key.
SECRET_KEYS=(
  "application.secret"
  "session.cookie.secret"
  "session.cookie.key"
  "authentication.cookie.secret"
  "authentication.cookie.key"
  "flash.cookie.secret"
  "flash.cookie.key"
  "api.accessToken.secret"
  "api.accessToken.key"
  "api.refreshToken.secret"
  "api.refreshToken.key"
  "api.challengeToken.secret"
  "api.challengeToken.key"
  "persistence.mongo.password"
)

# LC_ALL=C, because tr fails with "Illegal byte sequence" on systems without a C.UTF-8 locale
# (macOS, BSD) and would then silently produce an empty secret.
# The pipeline is guarded, as head closes the pipe and tr exits with SIGPIPE, which would abort
# the script through pipefail.
generate_secret() {
  local secret
  secret="$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom 2>/dev/null | head -c 64)" || true

  if [ "${#secret}" -ne 64 ]; then
    echo "Failed to generate a random secret. Is /dev/urandom readable?" >&2
    exit 1
  fi

  printf '%s' "$secret"
}

# Fails on HTTP errors instead of silently writing the error page to disk
download() {
  curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 --output "$2" "$1"
}

read_env() {
  local key="$1"
  local file="$2"
  local line value

  line="$(grep -E "^${key}=" "$file" 2>/dev/null | tail -n 1 || true)"
  value="${line#"${key}"=}"
  value="${value%$'\r'}"
  printf '%s' "$value"
}

# An existing installation kept these values in .env. Reuse them so cookie,
# API and database secrets stay valid across the move to secret files.
migrated_secret() {
  local key="$1"

  if [ ! -f .env ]; then
    printf ''
    return
  fi

  case "$key" in
    application.secret) read_env APPLICATION_SECRET .env ;;
    session.cookie.secret) read_env SESSION_COOKIE_SECRET .env ;;
    session.cookie.key) read_env SESSION_COOKIE_KEY .env ;;
    authentication.cookie.secret) read_env AUTHENTICATION_COOKIE_SECRET .env ;;
    authentication.cookie.key) read_env AUTHENTICATION_COOKIE_KEY .env ;;
    flash.cookie.secret) read_env FLASH_COOKIE_SECRET .env ;;
    flash.cookie.key) read_env FLASH_COOKIE_KEY .env ;;
    api.accessToken.secret) read_env API_ACCESSTOKEN_SECRET .env ;;
    api.accessToken.key) read_env API_ACCESSTOKEN_KEY .env ;;
    api.refreshToken.secret) read_env API_REFRESHTOKEN_SECRET .env ;;
    api.refreshToken.key) read_env API_REFRESHTOKEN_KEY .env ;;
    api.challengeToken.secret) read_env API_CHALLENGETOKEN_SECRET .env ;;
    api.challengeToken.key) read_env API_CHALLENGETOKEN_KEY .env ;;
    persistence.mongo.password)
      local password
      password="$(read_env MONGODB_INITDB_ROOT_PASSWORD .env)"
      if [ -z "$password" ]; then
        password="$(read_env PERSISTENCE_MONGO_PASSWORD .env)"
      fi
      printf '%s' "$password"
      ;;
    *)
      echo "Unknown secret key: ${key}" >&2
      exit 1
      ;;
  esac
}

write_secret_file() {
  local path="$1"
  local value="$2"

  if [ -z "$value" ]; then
    echo "Refusing to write an empty secret to ${path}" >&2
    exit 1
  fi

  printf '%s' "$value" > "$path"
  chmod 644 "$path"
}

read_secret_file() {
  local path="$1"
  printf '%s' "$(cat "$path")"
}

ensure_env() {
  local key="$1"
  local value="$2"
  local comment="$3"

  if grep -qE "^${key}=" .env; then
    return
  fi

  if [ -n "$comment" ]; then
    printf '\n%s\n%s=%s\n' "$comment" "$key" "$value" >> .env
  else
    printf '%s=%s\n' "$key" "$value" >> .env
  fi
  echo "    Added ${key}=${value} to .env"
}

echo "1/5 Creating secret files..."
mkdir -p "$SECRETS_DIR"
chmod 755 "$SECRETS_DIR"

created=0
migrated=0
for key in "${SECRET_KEYS[@]}"; do
  path="${SECRETS_DIR}/${key}"
  if [ -s "$path" ]; then
    chmod 644 "$path"
    continue
  fi

  existing="$(migrated_secret "$key")"
  if [ -n "$existing" ]; then
    write_secret_file "$path" "$existing"
    echo "    ${key} copied from .env"
    migrated=1
  else
    write_secret_file "$path" "$(generate_secret)"
    echo "    ${key} generated"
  fi
  created=1
done

smtp_path="${SECRETS_DIR}/smtp.password"
if [ -s "$smtp_path" ]; then
  chmod 644 "$smtp_path"
else
  smtp_password=""
  if [ -f .env ]; then
    smtp_password="$(read_env SMTP_PASSWORD .env)"
  fi
  if [ -n "$smtp_password" ]; then
    write_secret_file "$smtp_path" "$smtp_password"
    echo "    smtp.password copied from .env"
    migrated=1
    created=1
  else
    # Same placeholder the previous .env shipped. The operator replaces it before sending mail.
    write_secret_file "$smtp_path" "password"
    echo "    smtp.password set to the placeholder 'password'. Replace it with the mailbox password."
    created=1
  fi
fi

if [ "$created" -eq 0 ]; then
  echo "    [Skipping] secret files already exist."
elif [ "$migrated" -eq 1 ]; then
  echo "    Existing values were copied from .env into ${SECRETS_DIR}/."
  echo "    The application reads those files. MONGODB_INITDB_ROOT_PASSWORD in .env is still used by the database container."
fi

if [ -f .env ] && [ -s "${SECRETS_DIR}/persistence.mongo.password" ]; then
  env_password="$(read_env MONGODB_INITDB_ROOT_PASSWORD .env)"
  file_password="$(read_secret_file "${SECRETS_DIR}/persistence.mongo.password")"
  if [ -n "$env_password" ] && [ "$env_password" != "$file_password" ]; then
    echo "    Warning: MONGODB_INITDB_ROOT_PASSWORD in .env differs from ${SECRETS_DIR}/persistence.mongo.password." >&2
    echo "    The database container uses .env and the application reads the secret file. They must match." >&2
  fi
fi

if [ -f .env ] && [ -s "${SECRETS_DIR}/smtp.password" ]; then
  env_smtp="$(read_env SMTP_PASSWORD .env)"
  file_smtp="$(read_secret_file "${SECRETS_DIR}/smtp.password")"
  if [ -n "$env_smtp" ] && [ "$env_smtp" != "$file_smtp" ]; then
    echo "    Warning: SMTP_PASSWORD in .env differs from ${SECRETS_DIR}/smtp.password." >&2
    echo "    The application reads the secret file. SMTP_PASSWORD in .env is not used." >&2
  fi
fi

if [ -f ".env" ]; then
  echo "2/5 [Skipping] .env already exists - not regenerating it."
  echo "    Remove it manually if you really want a fresh installation."
  ensure_env "PERSISTENCE_MONGO_HOST" "filedpapers-database" "# Hostname of MongoDB. filedpapers-database is the service in compose.yaml."
  ensure_env "CONNECTOR_HTTP_PORT" "9090" "# Port the application listens on inside the container. It must match the container port in compose.yaml."
else
  echo "2/5 Creating .env file..."

  mongo_password="$(read_secret_file "${SECRETS_DIR}/persistence.mongo.password")"
  if [ -z "$mongo_password" ]; then
    echo "Failed to read the MongoDB password from ${SECRETS_DIR}/persistence.mongo.password" >&2
    exit 1
  fi

  cat > .env <<EOL
# Custom configuration
VERSION=latest
APPLICATION_URL=http://localhost
APPLICATION_REGISTRATION=true
APPLICATION_TRASH_RETENTION=72
# Port the application listens on inside the container. It must match the
# container port in compose.yaml, for example 127.0.0.1:4747:4747 needs 4747.
CONNECTOR_HTTP_PORT=9090
# Hostname of MongoDB. filedpapers-database is the service in compose.yaml.
# Set another host when the database is not the one started by compose.
PERSISTENCE_MONGO_HOST=filedpapers-database
SMTP_HOST=localhost
SMTP_PORT=25
SMTP_AUTHENTICATION=true
SMTP_USERNAME=username
SMTP_FROM=email@localhost
SMTP_PROTOCOL=smtptls
SMTP_DEBUG=false
# The mailbox password is secrets/smtp.password, not a variable in this file.

# Auto generated - Change at your own risk
# The database container reads the MONGODB_INITDB_* variables. The application
# reads the same password from secrets/persistence.mongo.password and the
# username from PERSISTENCE_MONGO_USERNAME. Keep the two usernames identical.
MONGODB_INITDB_DATABASE=filedpapers
MONGODB_INITDB_ROOT_USERNAME=filedpapers
MONGODB_INITDB_ROOT_PASSWORD=${mongo_password}
PERSISTENCE_MONGO_USERNAME=filedpapers
EOL

  chmod 600 .env
fi

if [ ! -d "logs" ]; then
  echo "3/5 Creating logs folder..."
  mkdir "logs"
else
  echo "3/5 [Skipping] Logs folder already exists."
fi
# umask 077 would leave this directory mode 700, owned by whoever ran the script.
# The container user is uid 33 and creates /logs/filedpapers.log. 1777 lets that
# user create the file; the sticky bit stops others from deleting it.
chmod 1777 "logs"

echo "4/5 Downloading compose.yaml..."
download "$COMPOSE_URL" "compose.yaml"

if [ -f "config/config.yaml" ]; then
  echo "    Note: config/config.yaml is no longer used. The application ships its own config.yaml."
  echo "    You can remove the config directory."
fi

echo "5/5 Installation complete!"
echo ""
echo "Set APPLICATION_URL and the SMTP_* values in .env, and put the mailbox password in secrets/smtp.password."
echo "Then adjust the published host and port in compose.yaml if needed."
echo "Enjoy Filed Papers!"