From a20c53b72e8e6b384cc57076e4a4fb2f8c973d0f Mon Sep 17 00:00:00 2001 From: WyzeWare Date: Tue, 20 Aug 2024 11:16:08 +0200 Subject: [PATCH] - Removed shell postinst script responsibilities for database setup, including the creation of the first-time setup token and related hashing functions. - Simplified deployment by centralizing migration management within the application. --- go.mod | 8 + go.sum | 11 + packaging/scripts/postinst | 667 +++++++++---------------------------- 3 files changed, 172 insertions(+), 514 deletions(-) diff --git a/go.mod b/go.mod index 42e73ae..38a144f 100644 --- a/go.mod +++ b/go.mod @@ -20,9 +20,17 @@ require ( modernc.org/sqlite v1.22.1 ) +require ( + github.com/golang-migrate/migrate/v4 v4.17.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + go.uber.org/atomic v1.11.0 // indirect +) + require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/golang-migrate/migrate v3.5.4+incompatible github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect diff --git a/go.sum b/go.sum index 7156601..37b7ff6 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,10 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA= +github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk= +github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= +github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= @@ -18,6 +22,11 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -62,6 +71,8 @@ go.mau.fi/util v0.4.1 h1:3EC9KxIXo5+h869zDGf5OOZklRd/FjeVnimTwtm3owg= go.mau.fi/util v0.4.1/go.mod h1:GjkTEBsehYZbSh2LlE6cWEn+6ZIZTGrTMM/5DMNlmFY= go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c h1:a5O4nqmwUWvmC+27RUdefkuy5XzMOEUqR9ji+/BcHZA= go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c/go.mod h1:kNI5foyzqd77d5HaWc1Jico6/rxtZ/UE8nr80hIsbIk= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= diff --git a/packaging/scripts/postinst b/packaging/scripts/postinst index 8041747..cf06768 100755 --- a/packaging/scripts/postinst +++ b/packaging/scripts/postinst @@ -8,12 +8,10 @@ SALT_FILE="/var/lib/wuzapi/salt" LOG_FILE="/var/log/wuzapi/postinstall.log" USER="wuzapi" TIMEOUT=30 # Timeout in seconds for user prompts -# flag to track if a new database was created -NEW_DB_CREATED=false # Ensure log directory exists and is writable sudo mkdir -p "$(dirname "$LOG_FILE")" || { echo "Failed to create log directory"; exit 1; } -sudo chown -R $USER:$USER "$(dirname "$LOG_FILE")" || { echo "Failed to change ownership of log directory"; exit 1; } +sudo chown -R $USER:$USER "$(dirname "$LOG_FILE")" || { echo "Failed to change ownership of log directory"; exit 1; } sudo chmod 755 "$(dirname "$LOG_FILE")" || { echo "Failed to set permissions on log directory"; exit 1; } # Redirect output to log file @@ -45,6 +43,7 @@ prompt_yes_no() { esac done } + choose_database_message() { echo "Please choose a database:" echo "1) PostgreSQL (default)" @@ -68,166 +67,138 @@ choose_database() { done } -generate_salt() { - # Ensure directory exists and has appropriate permissions - sudo mkdir -p "$(dirname "$SALT_FILE")" || { log "Failed to create directory for SALT file"; exit 1; } - sudo chown -R $USER:$USER "$(dirname "$SALT_FILE")" || { log "Failed to change ownership of SALT directory"; exit 1; } - sudo chmod -R 755 "$(dirname "$SALT_FILE")" || { log "Failed to set permissions for SALT directory"; exit 1; } - - # Remove the salt file if it already exists - if [ -f "$SALT_FILE" ]; then - sudo rm -f "$SALT_FILE" || { log "Failed to delete existing SALT file"; exit 1; } - fi - - # Generate a 16-byte random salt and save it to the salt file - local SALT=$(openssl rand -base64 16) - echo "$SALT" | sudo tee "$SALT_FILE" > /dev/null || { log "Failed to write salt to file"; exit 1; } - - # Set permissions for the salt file - sudo chown -R $USER:$USER "$SALT_FILE" || { log "Failed to set permissions for SALT file"; exit 1; } - sudo chmod 600 "$SALT_FILE" || { log "Failed to set permissions for SALT file"; exit 1; } -} - -generate_setup_token() { - local token=$(openssl rand -hex 32) - local expiry=$(date -u -d "+24 hours" +"%Y-%m-%d %H:%M:%S") - - # Hashing a password - local salt=$(cat "$SALT_FILE") +configure_sqlite() { + log "Configuring SQLite3..." - # Hashing function using SHA-256 - hash() { - echo -n "$2$1" | openssl dgst -sha256 -hex | awk '{print $2}' + create_directory() { + local dir_path=$1 + sudo mkdir -p "$dir_path" || { log "Failed to create directory: $dir_path"; exit 1; } + sudo chown -R $USER:$USER "$dir_path" || { log "Failed to change ownership of directory: $dir_path"; exit 1; } + sudo chmod 755 "$dir_path" || { log "Failed to set permissions for directory: $dir_path"; exit 1; } } - # Hash the token with the salt - local hashed_token=$(hash "$token" "$salt") - local base64_encoded_hashed_token=$(echo -n "$hashed_token" | tr -d '[:space:]' | base64 | tr -d '[:space:]') + apply_migrations() { + local db_path=$1 + log "Applying migrations to database at $db_path" + if sudo -u wuzapi sqlite3 "$db_path" < "path/to/migrations/migration_001_create_users_table.sql"; then + log "Migrations applied successfully" + sudo chown -R $USER:$USER "$db_path" + sudo chmod 640 "$db_path" + log "Permissions set on $db_path" + else + log "Failed to apply migrations to database at $db_path" + exit 1 + fi + } - # Return the values as separate lines - echo "TOKEN=$token" - echo "ENCODED_HASHED_TOKEN=$base64_encoded_hashed_token" - echo "EXPIRY=$expiry" -} + handle_existing_database() { + local db_path=$1 + log "SQLite database already exists at $db_path" -configure_sqlite() { - log "Configuring SQLite3..." - - # Prompt user if they want multiple organizations - if prompt_yes_no "Do you want to configure multiple organizations?"; then - log "Note: SQLite does not support multiple organizations. Proceeding with single-organization setup." - fi - sudo mkdir -p "$(dirname "$SQLITE_APP_DB_PATH")" || { log "Failed to create directory for SQLite database"; exit 1; } - sudo chown -R $USER:$USER "$(dirname "$SQLITE_APP_DB_PATH")" || { log "Failed to change ownership of SQLite database directory"; exit 1; } - sudo chmod 755 "$(dirname "$SQLITE_APP_DB_PATH")" || { log "Failed to set permissions for SQLite database directory"; exit 1; } - - sudo mkdir -p "$(dirname "$SQLITE_WA_DB_PATH")" || { log "Failed to create directory for SQLite database"; exit 1; } - sudo chown -R $USER:$USER "$(dirname "$SQLITE_WA_DB_PATH")" || { log "Failed to change ownership of SQLite database directory"; exit 1; } - sudo chmod 755 "$(dirname "$SQLITE_WA_DB_PATH")" || { log "Failed to set permissions for SQLite database directory"; exit 1; } - -if [ ! -f "$SQLITE_APP_DB_PATH" ]; then - log "Creating new SQLite database at $SQLITE_APP_DB_PATH" - if sudo -u wuzapi sqlite3 "$SQLITE_APP_DB_PATH" <= 59 AND length(token) <= 100), - webhook TEXT DEFAULT '', - jid TEXT DEFAULT '', - qrcode TEXT DEFAULT '', - connected INTEGER, - expiration INTEGER, - events TEXT DEFAULT 'All' -); -EOF - then - log "SQLite database created successfully with unique token" - sudo chown -R $USER:$USER "$SQLITE_APP_DB_PATH" - sudo chmod 640 "$SQLITE_APP_DB_PATH" - NEW_DB_CREATED=true - log "Permissions set on $SQLITE_APP_DB_PATH" - else - log "Failed to create SQLite database" - exit 1 - fi -else - log "SQLite database already exists at $SQLITE_APP_DB_PATH" - - # Add UNIQUE constraint to existing table if it doesn't already exist - if ! sudo -u wuzapi sqlite3 "$SQLITE_APP_DB_PATH" "PRAGMA index_list('users');" | grep -q "token"; then - log "Adding UNIQUE constraint to token in existing database" - if sudo -u wuzapi sqlite3 "$SQLITE_APP_DB_PATH" </dev/null; then - log "User 'wuzapi' exists" - user_info=$(id wuzapi) - log "User details: $user_info" -else - log "Error: User 'wuzapi' does not exist." - log "Attempting to create user 'wuzapi'..." - if sudo useradd -r -s /bin/false wuzapi; then - log "User 'wuzapi' created successfully" + # Database setup + if [ ! -f "$SQLITE_APP_DB_PATH" ]; then + apply_migrations "$SQLITE_APP_DB_PATH" else - log "Failed to create user 'wuzapi'. Please create this user manually and run the script again." - exit 1 + handle_existing_database "$SQLITE_APP_DB_PATH" fi -fi -# Verify user's ability to access required directories -if sudo -u wuzapi test -w /var/lib/wuzapi; then - log "User 'wuzapi' has write access to /var/lib/wuzapi" -else - log "Error: User 'wuzapi' does not have write access to /var/lib/wuzapi" - log "Attempting to fix permissions..." - sudo chown -R -R $USER:$USER /var/lib/wuzapi - sudo chmod -R 755 /var/lib/wuzapi - if sudo -u wuzapi test -w /var/lib/wuzapi; then - log "Permissions fixed successfully" - else - log "Failed to fix permissions. Please check and fix manually." + # Ensure the SQLite database was created + if [ ! -f "$SQLITE_APP_DB_PATH" ]; then + log "Failed to create SQLite database. Please check your permissions and SQLite installation." exit 1 fi -fi + # Save configuration echo "DB_TYPE=sqlite3" > "$CONFIG_FILE" - # In the configure_sqlite function: echo "APP_DB_PATH=$SQLITE_APP_DB_PATH" >> "$CONFIG_FILE" echo "WA_DB_PATH=$SQLITE_WA_DB_PATH" >> "$CONFIG_FILE" echo "SQLITE_FOREIGN_KEYS=ON" >> "$CONFIG_FILE" log "SQLite3 configuration saved." } + +# Common function to create databases and users +create_db_and_user() { + local db_name=$1 + local db_user=$2 + local db_password=$3 + + # Function to execute PostgreSQL commands + pg_execute() { + sudo -u postgres psql -c "$1" + } + + # Check if database exists + if pg_execute "SELECT 1 FROM pg_database WHERE datname='$db_name'" | grep -q 1; then + log "Database '$db_name' exists." + if prompt_yes_no "Do you want to drop and recreate the '$db_name' database?"; then + log "Dropping database '$db_name'..." + pg_execute "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$db_name';" + pg_execute "DROP DATABASE IF EXISTS $db_name;" + else + log "Continuing with the existing '$db_name' database." + fi + fi + + # Create the database + if ! pg_execute "SELECT 1 FROM pg_database WHERE datname='$db_name'" | grep -q 1; then + log "Creating database '$db_name'..." + pg_execute "CREATE DATABASE $db_name OWNER $db_user;" + fi + + # Check if user exists + if pg_execute "SELECT 1 FROM pg_roles WHERE rolname='$db_user'" | grep -q 1; then + log "User '$db_user' exists." + if prompt_yes_no "Do you want to delete and recreate the '$db_user' user?"; then + log "Deleting user '$db_user'..." + pg_execute "REASSIGN OWNED BY $db_user TO postgres;" + pg_execute "DROP OWNED BY $db_user;" + pg_execute "DROP USER IF EXISTS $db_user;" + log "Creating user '$db_user'..." + pg_execute "CREATE USER $db_user WITH PASSWORD '$db_password';" + else + log "Continuing with the existing '$db_user' user." + if prompt_yes_no "Do you want to set a new password for the '$db_user' user?"; then + pg_execute "ALTER USER $db_user WITH PASSWORD '$db_password';" + else + read -t $TIMEOUT -s -p "Enter the current password for '$db_user' user: " db_password + if [ $? -ne 0 ]; then + echo "No input received within $TIMEOUT seconds, exiting." + exit 1 + fi + echo + fi + fi + else + log "Creating user '$db_user'..." + pg_execute "CREATE USER $db_user WITH PASSWORD '$db_password';" + fi + pg_execute "ALTER DATABASE $db_name OWNER TO $db_user;" + pg_execute "GRANT ALL PRIVILEGES ON DATABASE $db_name TO $db_user;" + log "User '$db_user' is now the owner of database '$db_name' and has been granted all privileges." + log "User '$db_user' granted privileges on database '$db_name'." +} + configure_postgres() { if [ -f "$POSTGRES_CONFIG_FILE" ]; then log "An existing PostgreSQL configuration file was found at $POSTGRES_CONFIG_FILE" @@ -249,391 +220,59 @@ configure_postgres() { log "Successfully connected to local PostgreSQL instance." wuzapi_password=$(openssl rand -base64 48) - # Function to execute PostgreSQL commands - pg_execute() { - sudo -u postgres psql -c "$1" - } - - # Function to execute PostgreSQL commands on a specific database - pg_execute_db() { - sudo -u postgres psql -d "$1" -c "$2" - } - - # Terminate connections and drop databases if they exist - for db in wuzapi_app wuzapi_wa; do - if pg_execute "SELECT 1 FROM pg_database WHERE datname='$db'" | grep -q 1; then - log "Database '$db' exists." - if prompt_yes_no "Do you want to drop and recreate the '$db' database?"; then - log "Dropping database '$db'..." - pg_execute "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$db';" - pg_execute "DROP DATABASE IF EXISTS $db;" - else - log "Continuing with the existing '$db' database." - fi - fi - done - - # Now handle the user - if pg_execute "SELECT 1 FROM pg_roles WHERE rolname='wuzapi'" | grep -q 1; then - log "User 'wuzapi' exists." - if prompt_yes_no "Do you want to delete and recreate the 'wuzapi' user?"; then - log "Deleting user 'wuzapi'..." - pg_execute "REASSIGN OWNED BY wuzapi TO postgres;" - pg_execute "DROP OWNED BY wuzapi;" - pg_execute "DROP USER IF EXISTS wuzapi;" - log "Creating user 'wuzapi'..." - pg_execute "CREATE USER wuzapi WITH PASSWORD '$wuzapi_password';" - else - log "Continuing with the existing 'wuzapi' user." - if prompt_yes_no "Do you want to set a new password for the 'wuzapi' user?"; then - pg_execute "ALTER USER wuzapi WITH PASSWORD '$wuzapi_password';" - else - read -t $TIMEOUT -s -p "Enter the current password for 'wuzapi' user: " wuzapi_password - if [ $? -ne 0 ]; then - echo "No input received within $TIMEOUT seconds, exiting." - exit 1 - fi - echo - fi - fi - else - log "Creating user 'wuzapi'..." - pg_execute "CREATE USER wuzapi WITH PASSWORD '$wuzapi_password';" - fi - - # Now recreate the databases - for db in wuzapi_app wuzapi_wa; do - if ! pg_execute "SELECT 1 FROM pg_database WHERE datname='$db'" | grep -q 1; then - log "Creating database '$db'..." - pg_execute "CREATE DATABASE $db OWNER wuzapi;" - NEW_DB_CREATED=true - fi - log "Granting all privileges on '$db' database to 'wuzapi' user..." - pg_execute "GRANT ALL PRIVILEGES ON DATABASE $db TO wuzapi;" - done - - log "Creating schema and granting privileges for wuzapi_app..." - - pg_execute_db "wuzapi_app" " - CREATE SCHEMA IF NOT EXISTS wuzapi; - GRANT ALL PRIVILEGES ON SCHEMA wuzapi TO wuzapi; - - -- Grant privileges on existing tables (if any) - GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA wuzapi TO wuzapi; - - -- Grant privileges on existing sequences (if any) - GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA wuzapi TO wuzapi; - - -- Set default privileges for future tables - ALTER DEFAULT PRIVILEGES IN SCHEMA wuzapi - GRANT ALL PRIVILEGES ON TABLES TO wuzapi; - - -- Set default privileges for future sequences - ALTER DEFAULT PRIVILEGES IN SCHEMA wuzapi - GRANT ALL PRIVILEGES ON SEQUENCES TO wuzapi; - " - - log "Creating users table in wuzapi_app..." - if pg_execute_db "wuzapi_app" " - CREATE TABLE IF NOT EXISTS setup_token ( - id SERIAL PRIMARY KEY, - token TEXT NOT NULL UNIQUE CHECK(length(token) >= 59 AND length(token) <= 100), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - expires_at TIMESTAMP NOT NULL - ); - - GRANT ALL PRIVILEGES ON TABLE setup_token TO wuzapi; - - CREATE TABLE IF NOT EXISTS wuzapi.super_organizations ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL UNIQUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS wuzapi.organizations ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL UNIQUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS wuzapi.users ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL, - token TEXT NOT NULL UNIQUE CHECK(length(token) >= 59 AND length(token) <= 100), - webhook TEXT DEFAULT '', - jid TEXT DEFAULT '', - qrcode TEXT DEFAULT '', - connected INTEGER, - expiration INTEGER, - events TEXT DEFAULT 'All', - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - is_super_admin BOOLEAN DEFAULT FALSE, - is_admin BOOLEAN DEFAULT FALSE, - super_organization_id INTEGER, - CONSTRAINT fk_super_organization FOREIGN KEY (super_organization_id) - REFERENCES wuzapi.super_organizations(id) ON DELETE RESTRICT - ); - - CREATE TABLE IF NOT EXISTS wuzapi.user_organizations ( - user_id INTEGER NOT NULL, - organization_id INTEGER NOT NULL, - PRIMARY KEY (user_id, organization_id), - FOREIGN KEY (user_id) REFERENCES wuzapi.users(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES wuzapi.organizations(id) ON DELETE RESTRICT, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS wuzapi.organization_admins ( - user_id INTEGER NOT NULL, - organization_id INTEGER NOT NULL, - PRIMARY KEY (user_id, organization_id), - FOREIGN KEY (user_id) REFERENCES wuzapi.users(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES wuzapi.organizations(id) ON DELETE CASCADE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS wuzapi.super_organization_admins ( - user_id INTEGER NOT NULL, - super_organization_id INTEGER NOT NULL, - PRIMARY KEY (user_id, super_organization_id), - FOREIGN KEY (user_id) REFERENCES wuzapi.users(id) ON DELETE CASCADE, - FOREIGN KEY (super_organization_id) REFERENCES wuzapi.super_organizations(id) ON DELETE CASCADE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP - ); - - -- Function to check if users exist - CREATE OR REPLACE FUNCTION wuzapi.users_exist() RETURNS BOOLEAN AS \$\$ - BEGIN - RETURN EXISTS (SELECT 1 FROM wuzapi.users); - END; - \$\$ LANGUAGE plpgsql; - - -- Ensure super admins belong to a super organization - CREATE OR REPLACE FUNCTION wuzapi.ensure_super_admin_belongs_to_super_org() RETURNS TRIGGER AS \$\$ - BEGIN - IF wuzapi.users_exist() THEN - IF NEW.is_super_admin AND NEW.super_organization_id IS NULL THEN - RAISE EXCEPTION 'Super admin must belong to a super organization when users exist'; - END IF; - END IF; - RETURN NEW; - END; - \$\$ LANGUAGE plpgsql; - - CREATE TRIGGER enforce_super_admin_super_org - BEFORE INSERT OR UPDATE ON wuzapi.users - FOR EACH ROW EXECUTE FUNCTION wuzapi.ensure_super_admin_belongs_to_super_org(); - - CREATE OR REPLACE FUNCTION wuzapi.ensure_super_organization_exists() RETURNS TRIGGER AS \$\$ - BEGIN - IF wuzapi.users_exist() THEN - IF NOT EXISTS (SELECT 1 FROM wuzapi.super_organizations) THEN - RAISE EXCEPTION 'At least one super organization must exist when users are present'; - END IF; - END IF; - RETURN OLD; - END; - \$\$ LANGUAGE plpgsql; - - CREATE TRIGGER ensure_super_org_exists - AFTER DELETE ON wuzapi.super_organizations - FOR EACH STATEMENT EXECUTE FUNCTION wuzapi.ensure_super_organization_exists(); - - CREATE OR REPLACE FUNCTION wuzapi.ensure_super_admin_exists() RETURNS TRIGGER AS \$\$ - BEGIN - IF wuzapi.users_exist() THEN - IF NOT EXISTS (SELECT 1 FROM wuzapi.users WHERE is_super_admin = TRUE) THEN - RAISE EXCEPTION 'At least one super admin must exist when users are present'; - END IF; - END IF; - RETURN OLD; - END; - \$\$ LANGUAGE plpgsql; - - CREATE TRIGGER prevent_delete_last_super_admin - AFTER DELETE OR UPDATE ON wuzapi.users - FOR EACH STATEMENT EXECUTE FUNCTION wuzapi.ensure_super_admin_exists(); - - -- Ensure each super organization has an admin - CREATE OR REPLACE FUNCTION wuzapi.ensure_super_org_has_admin() RETURNS TRIGGER AS \$\$ - BEGIN - IF wuzapi.users_exist() THEN - IF NOT EXISTS ( - SELECT 1 FROM wuzapi.super_organization_admins - WHERE super_organization_id = NEW.id - ) THEN - RAISE EXCEPTION 'Each super organization must have at least one admin when users exist'; - END IF; - END IF; - RETURN NEW; - END; - \$\$ LANGUAGE plpgsql; - - CREATE TRIGGER enforce_super_org_admin - AFTER INSERT OR UPDATE ON wuzapi.super_organizations - FOR EACH ROW EXECUTE FUNCTION wuzapi.ensure_super_org_has_admin(); - - -- Indexes - CREATE INDEX IF NOT EXISTS idx_users_token ON wuzapi.users(token); - CREATE INDEX IF NOT EXISTS idx_user_organizations_user ON wuzapi.user_organizations(user_id); - CREATE INDEX IF NOT EXISTS idx_user_organizations_org ON wuzapi.user_organizations(organization_id); - CREATE INDEX IF NOT EXISTS idx_users_super_admin ON wuzapi.users(is_super_admin); - CREATE INDEX IF NOT EXISTS idx_users_admin ON wuzapi.users(is_admin); - CREATE INDEX IF NOT EXISTS idx_super_org_admins_user ON wuzapi.super_organization_admins(user_id); - CREATE INDEX IF NOT EXISTS idx_super_org_admins_org ON wuzapi.super_organization_admins(super_organization_id); - "; then - log "PostgreSQL table created successfully with super organizations and super admin constraints." - else - log "Failed to create PostgreSQL tables." - exit 1 - fi + create_db_and_user "wuzapi_app" "wuzapi" "$wuzapi_password" + create_db_and_user "wuzapi_wa" "wuzapi" "$wuzapi_password" - log "Creating schema for wuzapi_wa..." - pg_execute_db "wuzapi_wa" " - CREATE SCHEMA IF NOT EXISTS whatsmeow; - GRANT USAGE ON SCHEMA whatsmeow TO wuzapi; - GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA whatsmeow TO wuzapi; - GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA whatsmeow TO wuzapi; - - -- Set default privileges for future tables - ALTER DEFAULT PRIVILEGES IN SCHEMA whatsmeow - GRANT ALL PRIVILEGES ON TABLES TO wuzapi; - - -- Set default privileges for future sequences - ALTER DEFAULT PRIVILEGES IN SCHEMA whatsmeow - GRANT ALL PRIVILEGES ON SEQUENCES TO wuzapi; - " - - echo "HOST=localhost" > "$POSTGRES_CONFIG_FILE" - echo "USER=wuzapi" >> "$POSTGRES_CONFIG_FILE" - echo "PASSWORD=$wuzapi_password" >> "$POSTGRES_CONFIG_FILE" - echo "APP_DATABASE=wuzapi_app" >> "$POSTGRES_CONFIG_FILE" - echo "WA_DATABASE=wuzapi_wa" >> "$POSTGRES_CONFIG_FILE" - log "PostgreSQL configuration saved. $NEW_DB_CREATED" - - if [[ "$NEW_DB_CREATED" == "true" ]]; then - log "Creating setup tokens" - generate_salt || { log "Failed to generate salt"; exit 1; } - - # Capture the output of generate_setup_token - while IFS='=' read -r key value; do - case "$key" in - TOKEN) token="$value" ;; - ENCODED_HASHED_TOKEN) base64_encoded_hashed_token="$value" ;; - EXPIRY) expiry="$value" ;; - esac - done < <(generate_setup_token) - - pg_execute_db "wuzapi_app" "INSERT INTO setup_token (token, expires_at) VALUES ('$base64_encoded_hashed_token', '$expiry');" - - log "Setup token generated and saved to the database" - echo "Use this token to create the first super admin: $token" - echo "This token will expire in 24 hours. (at: $expiry)" - fi + echo "DB_TYPE=postgresql" > "$CONFIG_FILE" + echo "POSTGRES_USER=wuzapi" >> "$CONFIG_FILE" + echo "POSTGRES_PASSWORD=$wuzapi_password" >> "$CONFIG_FILE" + log "PostgreSQL configuration saved." else + log "Local PostgreSQL instance not used. Proceeding with custom configuration." custom_postgres_config fi } - custom_postgres_config() { - log "Please provide PostgreSQL configuration details:" - read -t $TIMEOUT -p "Host: " pg_host - if [ $? -ne 0 ]; then - echo "No input received within $TIMEOUT seconds, exiting." - exit 1 - fi - read -t $TIMEOUT -p "Username: " pg_user - if [ $? -ne 0 ]; then - echo "No input received within $TIMEOUT seconds, exiting." - exit 1 - fi - read -t $TIMEOUT -s -p "Password: " pg_password - if [ $? -ne 0 ]; then - echo "No input received within $TIMEOUT seconds, exiting." - exit 1 - fi - echo - - echo "HOST=$pg_host" > "$POSTGRES_CONFIG_FILE" - echo "USER=$pg_user" >> "$POSTGRES_CONFIG_FILE" - echo "PASSWORD=$pg_password" >> "$POSTGRES_CONFIG_FILE" - log "Custom PostgreSQL configuration saved." -} - -if [ -f "$CONFIG_FILE" ]; then - log "An existing configuration file was found at $CONFIG_FILE" - if prompt_yes_no "Do you want to override the existing configuration?"; then - log "Proceeding with new configuration..." - else - log "Keeping existing configuration. Installation completed." - exit 0 - fi -fi - -DB_CHOICE=$(choose_database) -mkdir -p /etc/wuzapi || { log "Failed to create configuration directory"; exit 1; } -echo "DB_TYPE=$DB_CHOICE" > "$CONFIG_FILE" - -if [ "$DB_CHOICE" = "postgresql" ]; then - configure_postgres -elif [ "$DB_CHOICE" = "sqlite3" ]; then - configure_sqlite -fi + log "Starting custom PostgreSQL configuration..." -log "Database choice ($DB_CHOICE) has been saved to $CONFIG_FILE" - -if [ "$DB_CHOICE" = "postgresql" ]; then - log "PostgreSQL configuration has been saved to $POSTGRES_CONFIG_FILE" -elif [ "$DB_CHOICE" = "sqlite3" ]; then - log "SQLite3 configuration has been saved to $CONFIG_FILE" -fi - -if [ ! -f "/etc/systemd/system/wuzapi.service" ]; then - log "Creating systemd service file" - sudo tee /etc/systemd/system/wuzapi.service > /dev/null < "$CONFIG_FILE" + echo "POSTGRES_HOST=$host" >> "$CONFIG_FILE" + echo "POSTGRES_PORT=$port" >> "$CONFIG_FILE" + echo "POSTGRES_USER=$db_user" >> "$CONFIG_FILE" + echo "POSTGRES_PASSWORD=$db_password" >> "$CONFIG_FILE" + log "Custom PostgreSQL configuration saved." +} -log "Wuzapi post-installation completed" -log "Log file is available at $LOG_FILE" +case $(choose_database) in + postgresql) + configure_postgres + ;; + sqlite3) + configure_sqlite + ;; + *) + log "Invalid choice. Exiting." + exit 1 + ;; +esac -echo "Wuzapi installation completed successfully!" +log "Post-installation script completed."