diff --git a/shell.nix b/shell.nix index 9c57bfb..bd2567e 100644 --- a/shell.nix +++ b/shell.nix @@ -21,22 +21,49 @@ pkgs.mkShell { MYSQL_SOCKET="$PWD/.mysql/mysql.sock" MYSQL_PIDFILE="$PWD/.mysql/mysql.pid" - mkdir -p "$MYSQL_DATADIR" + # + # Work out what database situation we are in BEFORE touching anything. + # This shell is used on the production server too, where a system MariaDB + # is already running -- we must not initialise a datadir, start a second + # server on port 3306, or inject dev credentials over the real ones. + # + # ours - the project-local server is already up (another nix-shell) + # existing - some other server is listening; not ours to manage + # start - nothing is running; this is a dev box, bring one up + # + SS_DB_MODE="start" - # Initialize data directory on first run - if [ ! -d "$MYSQL_DATADIR/mysql" ]; then - echo "Initializing MySQL data directory (first run)..." - mysql_install_db \ - --datadir="$MYSQL_DATADIR" \ - --skip-test-db \ - --auth-root-authentication-method=normal \ - 2>/dev/null - echo "Initialization complete." + if mysqladmin --socket="$MYSQL_SOCKET" ping &>/dev/null 2>&1; then + SS_DB_MODE="ours" + elif (echo >/dev/tcp/127.0.0.1/3306) &>/dev/null \ + || [ -S /run/mysqld/mysqld.sock ] \ + || [ -S /var/run/mysqld/mysqld.sock ] \ + || [ -S /tmp/mysql.sock ]; then + SS_DB_MODE="existing" + fi + + if [ "$SS_DB_MODE" = "existing" ]; then + echo "Existing database detected -- not starting the project one." + echo "Leaving DB_* and JSON_KEY alone so the server's own config wins." fi - # Start mysqld if not already running STARTED_MYSQL=0 - if ! mysqladmin --socket="$MYSQL_SOCKET" ping &>/dev/null 2>&1; then + + if [ "$SS_DB_MODE" = "start" ]; then + + mkdir -p "$MYSQL_DATADIR" + + # Initialize data directory on first run + if [ ! -d "$MYSQL_DATADIR/mysql" ]; then + echo "Initializing MySQL data directory (first run)..." + mysql_install_db \ + --datadir="$MYSQL_DATADIR" \ + --skip-test-db \ + --auth-root-authentication-method=normal \ + 2>/dev/null + echo "Initialization complete." + fi + STARTED_MYSQL=1 echo "Starting MySQL..." mysqld --no-defaults \ @@ -56,20 +83,34 @@ pkgs.mkShell { -e "CREATE DATABASE IF NOT EXISTS application;" 2>/dev/null fi - # Set app DB env vars — dotenv won't override these since they're already in process.env - export DB_HOST=127.0.0.1 - export DB_USER=root - export DB_PASS= + # + # Dev credentials, injected ONLY when this shell is providing the database + # and there is no ~/.env supplying real config. + # + # This matters because dotenv does NOT override variables already present in + # process.env (server/config/database.js loads ~/.env). So anything exported + # here silently beats the server's real configuration -- including JSON_KEY, + # which would invalidate every existing user session. + # + if [ "$SS_DB_MODE" != "existing" ] && [ ! -f "$HOME/.env" ]; then - # Stable dev JWT key, persisted across shell sessions - if [ -z "''${JSON_KEY:-}" ]; then - if [ -s "$PWD/.mysql/jwt_key" ]; then - export JSON_KEY=$(cat "$PWD/.mysql/jwt_key") - else - export JSON_KEY=$(openssl rand -hex 32) - echo "$JSON_KEY" > "$PWD/.mysql/jwt_key" - echo "Generated dev JSON_KEY (saved to .mysql/jwt_key)" + export DB_HOST=127.0.0.1 + export DB_USER=root + export DB_PASS= + + # Stable dev JWT key, persisted across shell sessions + if [ -z "''${JSON_KEY:-}" ]; then + if [ -s "$PWD/.mysql/jwt_key" ]; then + export JSON_KEY=$(cat "$PWD/.mysql/jwt_key") + else + export JSON_KEY=$(openssl rand -hex 32) + echo "$JSON_KEY" > "$PWD/.mysql/jwt_key" + echo "Generated dev JSON_KEY (saved to .mysql/jwt_key)" + fi fi + + elif [ -f "$HOME/.env" ]; then + echo "~/.env found -- using its DB_* and JSON_KEY, not dev defaults." fi # Restore a SQL dump — handles both plain .sql and .gz files @@ -79,6 +120,16 @@ pkgs.mkShell { echo "Usage: restore-db " return 1 fi + + # Refuse to run against a database this shell does not manage. The dumps + # are --all-databases: restoring one would overwrite every schema on the + # server and reset root's password. That is fine for a local dev copy and + # catastrophic on the production box. + if [ "$SS_DB_MODE" = "existing" ]; then + echo "Refusing: this shell is talking to a pre-existing database." >&2 + echo "restore-db only runs against the project-local dev database." >&2 + return 1 + fi echo "Restoring database from $file ..." if [[ "$file" == *.gz ]]; then gunzip -c "$file" | mysql -u root --socket="$MYSQL_SOCKET" @@ -99,6 +150,7 @@ pkgs.mkShell { export -f restore-db # Auto-restore if --argstr backupFile was passed to nix-shell + # (restore-db itself refuses when the database is not ours) if [ -n "${backupFile}" ]; then restore-db "${backupFile}" fi