272 lines
13 KiB
Bash
272 lines
13 KiB
Bash
#!/bin/bash
|
|
set -e
|
|
|
|
ADMIN_LOGIN=${ADMIN_LOGIN:-admin}
|
|
ADMIN_PASSWORD=${ADMIN_PASSWORD:-LexP@ssw0rd_1}
|
|
|
|
# Ждем пока PostgreSQL будет готов принимать подключения
|
|
until pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"; do
|
|
echo "Waiting for PostgreSQL to be ready..."
|
|
sleep 2
|
|
done
|
|
|
|
# Создаем базу данных и пользователя если не существуют
|
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
-- Создаем расширение если не существует
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
|
|
-- Даем все права пользователю на базу данных
|
|
GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_DB TO $POSTGRES_USER;
|
|
|
|
-- Создаем таблицы если их нет (для первоначальной инициализации)
|
|
DO \$\$
|
|
BEGIN
|
|
-- Таблица приложений
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'application') THEN
|
|
CREATE TABLE application (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL,
|
|
host_ip VARCHAR(45) NOT NULL,
|
|
hostname VARCHAR(100) NOT NULL,
|
|
path VARCHAR(500) NOT NULL,
|
|
active BOOLEAN DEFAULT TRUE,
|
|
status VARCHAR(20) DEFAULT 'UNKNOWN',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Индексы для application
|
|
CREATE INDEX idx_application_host_ip ON application(host_ip);
|
|
CREATE INDEX idx_application_active ON application(active);
|
|
CREATE INDEX idx_application_status ON application(status);
|
|
CREATE INDEX idx_application_created_at ON application(created_at);
|
|
|
|
RAISE NOTICE 'Table application created successfully';
|
|
ELSE
|
|
RAISE NOTICE 'Table application already exists';
|
|
END IF;
|
|
|
|
-- Гарантируем наличие колонки status в application
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'application' AND column_name = 'status'
|
|
) THEN
|
|
ALTER TABLE application ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'UNKNOWN';
|
|
UPDATE application SET status = 'UNKNOWN' WHERE status IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_application_status ON application(status);
|
|
RAISE NOTICE 'Added status column to application table';
|
|
END IF;
|
|
|
|
-- Таблица docker-compose файлов
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'compose_file') THEN
|
|
CREATE TABLE compose_file (
|
|
id SERIAL PRIMARY KEY,
|
|
application_id INTEGER NOT NULL REFERENCES application(id) ON DELETE CASCADE,
|
|
content TEXT NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
is_current BOOLEAN DEFAULT TRUE
|
|
);
|
|
|
|
-- Индексы для compose_file
|
|
CREATE INDEX idx_compose_file_application_id ON compose_file(application_id);
|
|
CREATE INDEX idx_compose_file_is_current ON compose_file(is_current);
|
|
CREATE INDEX idx_compose_file_created_at ON compose_file(created_at);
|
|
|
|
RAISE NOTICE 'Table compose_file created successfully';
|
|
ELSE
|
|
RAISE NOTICE 'Table compose_file already exists';
|
|
END IF;
|
|
|
|
-- Таблица env файлов (обновленная структура)
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'env_file') THEN
|
|
CREATE TABLE env_file (
|
|
id SERIAL PRIMARY KEY,
|
|
application_id INTEGER NOT NULL REFERENCES application(id) ON DELETE CASCADE,
|
|
filename VARCHAR(100) NOT NULL DEFAULT '.env',
|
|
content TEXT NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
is_current BOOLEAN DEFAULT TRUE
|
|
);
|
|
|
|
-- Уникальный constraint ТОЛЬКО для текущих версий (используем partial unique index)
|
|
CREATE UNIQUE INDEX unique_current_env ON env_file (application_id, filename)
|
|
WHERE is_current = true;
|
|
|
|
-- Индексы для env_file
|
|
CREATE INDEX idx_env_file_application_id ON env_file(application_id);
|
|
CREATE INDEX idx_env_file_filename ON env_file(filename);
|
|
CREATE INDEX idx_env_file_is_current ON env_file(is_current);
|
|
CREATE INDEX idx_env_file_created_at ON env_file(created_at);
|
|
|
|
RAISE NOTICE 'Table env_file created successfully';
|
|
ELSE
|
|
-- Если таблица уже существует, проверяем наличие поля filename
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'env_file' AND column_name = 'filename') THEN
|
|
-- Добавляем поле filename
|
|
ALTER TABLE env_file ADD COLUMN filename VARCHAR(100) NOT NULL DEFAULT '.env';
|
|
|
|
-- Создаем индексы для нового поля
|
|
CREATE INDEX idx_env_file_filename ON env_file(filename);
|
|
|
|
RAISE NOTICE 'Added filename column to env_file table';
|
|
ELSE
|
|
RAISE NOTICE 'Table env_file already exists with filename column';
|
|
END IF;
|
|
|
|
-- Удаляем старый constraint если он существует
|
|
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
WHERE table_name = 'env_file' AND constraint_name = 'unique_current_env') THEN
|
|
ALTER TABLE env_file DROP CONSTRAINT unique_current_env;
|
|
RAISE NOTICE 'Dropped old unique_current_env constraint';
|
|
END IF;
|
|
|
|
-- Удаляем старый index если он существует
|
|
IF EXISTS (SELECT 1 FROM pg_indexes
|
|
WHERE tablename = 'env_file' AND indexname = 'unique_current_env') THEN
|
|
DROP INDEX unique_current_env;
|
|
RAISE NOTICE 'Dropped old unique_current_env index';
|
|
END IF;
|
|
|
|
-- Создаем новый partial unique index
|
|
CREATE UNIQUE INDEX unique_current_env ON env_file (application_id, filename)
|
|
WHERE is_current = true;
|
|
|
|
RAISE NOTICE 'Created new partial unique index for current env files';
|
|
END IF;
|
|
|
|
-- Таблица логов миграций
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'migration_log') THEN
|
|
CREATE TABLE migration_log (
|
|
id SERIAL PRIMARY KEY,
|
|
application_id INTEGER NOT NULL REFERENCES application(id) ON DELETE CASCADE,
|
|
command VARCHAR(500) NOT NULL,
|
|
status VARCHAR(50) NOT NULL,
|
|
output TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Индексы для migration_log
|
|
CREATE INDEX idx_migration_log_application_id ON migration_log(application_id);
|
|
CREATE INDEX idx_migration_log_created_at ON migration_log(created_at);
|
|
|
|
RAISE NOTICE 'Table migration_log created successfully';
|
|
ELSE
|
|
RAISE NOTICE 'Table migration_log already exists';
|
|
END IF;
|
|
|
|
-- Таблица задач агента
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'agent_task') THEN
|
|
CREATE TABLE agent_task (
|
|
id SERIAL PRIMARY KEY,
|
|
task_id VARCHAR(36) UNIQUE NOT NULL,
|
|
application_id INTEGER NOT NULL REFERENCES application(id) ON DELETE CASCADE,
|
|
task_type VARCHAR(50) NOT NULL,
|
|
command VARCHAR(500) NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
|
host_ip VARCHAR(45) NOT NULL,
|
|
payload TEXT,
|
|
output TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
completed_at TIMESTAMP
|
|
);
|
|
|
|
-- Создаем индексы для часто используемых полей
|
|
CREATE INDEX idx_agent_task_task_id ON agent_task(task_id);
|
|
CREATE INDEX idx_agent_task_application_id ON agent_task(application_id);
|
|
CREATE INDEX idx_agent_task_status ON agent_task(status);
|
|
CREATE INDEX idx_agent_task_host_ip ON agent_task(host_ip);
|
|
CREATE INDEX idx_agent_task_created_at ON agent_task(created_at);
|
|
CREATE INDEX idx_agent_task_completed_at ON agent_task(completed_at);
|
|
|
|
-- Комментарии к таблице и полям
|
|
COMMENT ON TABLE agent_task IS 'Таблица для хранения задач агента развертывания';
|
|
COMMENT ON COLUMN agent_task.task_id IS 'Уникальный идентификатор задачи (UUID)';
|
|
COMMENT ON COLUMN agent_task.task_type IS 'Тип задачи: deploy, migration, update_config';
|
|
COMMENT ON COLUMN agent_task.status IS 'Статус задачи: pending, in_progress, completed, failed';
|
|
COMMENT ON COLUMN agent_task.payload IS 'Дополнительные данные для задачи в формате JSON';
|
|
COMMENT ON COLUMN agent_task.output IS 'Вывод выполнения команды';
|
|
|
|
RAISE NOTICE 'Table agent_task created successfully';
|
|
ELSE
|
|
RAISE NOTICE 'Table agent_task already exists';
|
|
END IF;
|
|
|
|
-- Таблица пользователей веб-интерфейса
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'auth_user') THEN
|
|
CREATE TABLE auth_user (
|
|
id SERIAL PRIMARY KEY,
|
|
username VARCHAR(100) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(128) NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX idx_auth_user_username ON auth_user(username);
|
|
CREATE INDEX idx_auth_user_active ON auth_user(is_active);
|
|
RAISE NOTICE 'Table auth_user created successfully';
|
|
ELSE
|
|
RAISE NOTICE 'Table auth_user already exists';
|
|
END IF;
|
|
|
|
-- Обновляем длину поля path в application если нужно
|
|
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'application' AND column_name = 'path'
|
|
AND character_maximum_length < 500) THEN
|
|
ALTER TABLE application ALTER COLUMN path TYPE VARCHAR(500);
|
|
RAISE NOTICE 'Updated application.path column length to 500';
|
|
END IF;
|
|
|
|
-- Обновляем длину поля command в migration_log если нужно
|
|
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'migration_log' AND column_name = 'command'
|
|
AND character_maximum_length < 500) THEN
|
|
ALTER TABLE migration_log ALTER COLUMN command TYPE VARCHAR(500);
|
|
RAISE NOTICE 'Updated migration_log.command column length to 500';
|
|
END IF;
|
|
|
|
-- Обновляем длину поля command в agent_task если нужно
|
|
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'agent_task' AND column_name = 'command'
|
|
AND character_maximum_length < 500) THEN
|
|
ALTER TABLE agent_task ALTER COLUMN command TYPE VARCHAR(500);
|
|
RAISE NOTICE 'Updated agent_task.command column length to 500';
|
|
END IF;
|
|
|
|
END
|
|
\$\$;
|
|
|
|
-- Создаем функцию для автоматического обновления updated_at
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS \$\$
|
|
BEGIN
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
RETURN NEW;
|
|
END;
|
|
\$\$ language 'plpgsql';
|
|
|
|
-- Создаем триггер для автоматического обновления updated_at в application
|
|
DROP TRIGGER IF EXISTS update_application_updated_at ON application;
|
|
CREATE TRIGGER update_application_updated_at
|
|
BEFORE UPDATE ON application
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- Анализируем таблицы для оптимизации
|
|
ANALYZE application;
|
|
ANALYZE compose_file;
|
|
ANALYZE env_file;
|
|
ANALYZE migration_log;
|
|
ANALYZE agent_task;
|
|
ANALYZE auth_user;
|
|
|
|
-- Инициализация единственной учетной записи (логин/пароль задаются env ADMIN_LOGIN/ADMIN_PASSWORD)
|
|
INSERT INTO auth_user (username, password_hash, is_active)
|
|
VALUES ('$ADMIN_LOGIN', md5('$ADMIN_PASSWORD'), TRUE)
|
|
ON CONFLICT (username) DO UPDATE
|
|
SET password_hash = EXCLUDED.password_hash,
|
|
is_active = TRUE;
|
|
EOSQL
|
|
|
|
echo "Database initialization completed!" |