Files
SolidScribe/shell.nix
T

169 lines
5.6 KiB
Nix

{ backupFile ? "" }:
with import <nixpkgs> { };
pkgs.mkShell {
name = "Solid Scribe Env";
buildInputs = [
nodejs
nodemon
pnpm
pm2
mariadb
openssl
];
shellHook = ''
echo "Welcome to Solid Scribe"
alias npm=pnpm
MYSQL_DATADIR="$PWD/.mysql/data"
MYSQL_SOCKET="$PWD/.mysql/mysql.sock"
MYSQL_PIDFILE="$PWD/.mysql/mysql.pid"
#
# 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"
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
STARTED_MYSQL=0
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 \
--datadir="$MYSQL_DATADIR" \
--socket="$MYSQL_SOCKET" \
--pid-file="$MYSQL_PIDFILE" \
--port=3306 \
--bind-address=127.0.0.1 \
--log-error="$PWD/.mysql/error.log" &
until mysqladmin --socket="$MYSQL_SOCKET" ping &>/dev/null 2>&1; do
sleep 0.5
done
echo "MySQL ready."
mysql -u root --socket="$MYSQL_SOCKET" \
-e "CREATE DATABASE IF NOT EXISTS application;" 2>/dev/null
fi
#
# 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
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
restore-db() {
local file="''${1:-}"
if [ -z "$file" ]; then
echo "Usage: restore-db <backup.sql[.gz]>"
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"
else
mysql -u root --socket="$MYSQL_SOCKET" < "$file"
fi
# The dump is --all-databases, so it includes the `mysql` schema and
# overwrites local root with production's credentials. In-memory grants
# are still the old ones until FLUSH, so fix root now while we can
# still authenticate.
mysql -u root --socket="$MYSQL_SOCKET" -e "
ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD(''');
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;"
echo "Restore complete."
}
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
if [ "$STARTED_MYSQL" = "1" ]; then
trap "echo 'Stopping MySQL...'; mysqladmin -u root --socket=$MYSQL_SOCKET shutdown 2>/dev/null" EXIT
else
echo "MySQL already running (started by another shell) -- leaving it up on exit."
fi
# Do not start the app here.
# This same shell.nix is used for hosting the app on dev
# use the startDevServerAndClient.sh script
'';
}