Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
This commit is contained in:
@@ -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!"
|
||||
Reference in New Issue
Block a user