Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
ci / test (push) Successful in 2m22s
ci / lint (push) Successful in 4m31s

This commit is contained in:
Maksim Totmin
2026-10-07 20:13:23 +07:00
commit fdbd4a4fb6
54 changed files with 5672 additions and 0 deletions
+272
View File
@@ -0,0 +1,272 @@
#!/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!"