mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Start work on easy migration from one version to another using alembic
This commit is contained in:
parent
b97257c7c3
commit
9bc64d515d
83 changed files with 6620 additions and 678 deletions
|
|
@ -48,6 +48,7 @@ repos:
|
|||
- id: flake8
|
||||
name: Flake8 Python Linter
|
||||
args: ["--max-line-length=160", "--ignore=E266,E402,E501,E722,W503"]
|
||||
exclude: ^src/common/db/alembic/(mariadb|mysql|postgresql|sqlite)_versions
|
||||
|
||||
- repo: https://github.com/dosisod/refurb
|
||||
rev: 2e31f0033b6c00bf99912fc6a8b5fd00460c9ba0 # frozen: v2.0.0
|
||||
|
|
|
|||
34
misc/migration/Dockerfile
Normal file
34
misc/migration/Dockerfile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
FROM python:3.10-alpine@sha256:cf8ad3b55fcf5fa0cfeecabc65878f7504c8e6ec5c8af5511b0a039cc46a1ac3
|
||||
|
||||
# Install python dependencies
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
build-base libffi-dev postgresql-dev cargo
|
||||
|
||||
# Copy python requirements
|
||||
COPY src/deps/requirements.txt /tmp/requirements-deps.txt
|
||||
COPY src/common/db/requirements.txt /tmp/req/requirements-db.txt
|
||||
|
||||
WORKDIR /usr/share/migration
|
||||
|
||||
# Install python requirements
|
||||
RUN export MAKEFLAGS="-j$(nproc)" && \
|
||||
pip install --no-cache-dir --require-hashes --break-system-packages -r /tmp/requirements-deps.txt && \
|
||||
pip install --no-cache-dir --require-hashes -r /tmp/req/requirements-db.txt
|
||||
|
||||
# Install dependencies
|
||||
RUN apk add --no-cache bash sed curl mariadb-connector-c mariadb-client postgresql-client sqlite tzdata
|
||||
|
||||
# Cleanup
|
||||
RUN apk del .build-deps && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Copy files
|
||||
COPY src/common/db/alembic/alembic.ini alembic.ini
|
||||
COPY src/common/db/alembic/env.py env.py
|
||||
COPY src/common/db/alembic/script.py.mako script.py.mako
|
||||
COPY src/common/db/model.py latest_model.py
|
||||
COPY misc/migration/entrypoint.sh entrypoint.sh
|
||||
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
||||
ENTRYPOINT [ "./entrypoint.sh" ]
|
||||
175
misc/migration/create.sh
Executable file
175
misc/migration/create.sh
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Optimized migration script
|
||||
set -euo pipefail
|
||||
|
||||
log() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
# Fetch and process tags
|
||||
log "🌐 Fetching tags from GitHub"
|
||||
tags=$(curl -s https://api.github.com/repos/bunkerity/bunkerweb/tags |
|
||||
jq -r '.[].name | sub("^v"; "")' |
|
||||
jq -R -s -c 'split("\n")[:-1] | map(select(test("^[1-9]+\\.(5|[6-9]|[1-9][0-9]+)")))' |
|
||||
jq -c 'reverse')
|
||||
|
||||
current_dir=$(basename "$(pwd)")
|
||||
|
||||
# Navigate to the root directory if in a subdirectory
|
||||
case "$current_dir" in
|
||||
migration) cd ../.. ;;
|
||||
misc) cd .. ;;
|
||||
esac
|
||||
|
||||
if [[ ! -f src/VERSION ]]; then
|
||||
log "❌ src/VERSION file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read and validate the current version
|
||||
current_version=$(<src/VERSION)
|
||||
if [[ "$current_version" != "dev" && "$current_version" != "testing" ]]; then
|
||||
tags=$(echo "$tags" | jq -c --arg version "$current_version" 'if index($version) == null then . + [$version] else . end')
|
||||
fi
|
||||
|
||||
# Build the Docker image
|
||||
log "🐳 Building Docker image for migration"
|
||||
docker build -t local/bw-migration -f misc/migration/Dockerfile .
|
||||
|
||||
# Ensure we're in the migration directory
|
||||
cd misc/migration || exit 1
|
||||
|
||||
db_dir=$(realpath ../../src/common/db)
|
||||
|
||||
# Process each tag and database combination
|
||||
log "🏗️ Processing migration tags and databases"
|
||||
NEXT_TAG="dev"
|
||||
jq -r 'to_entries[] | "\(.key) \(.value)"' databases.json | while read -r database database_uri; do
|
||||
started=0
|
||||
|
||||
for tag in $(echo "$tags" | jq -r '.[]'); do
|
||||
if [ "$tag" == "$NEXT_TAG" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
export DATABASE="$database"
|
||||
export DATABASE_URI="${database_uri//+psycopg}"
|
||||
|
||||
if [[ "$started" -eq 0 ]]; then
|
||||
tag_index=$(echo "$tags" | jq -r --arg current_tag "$tag" 'index($current_tag)')
|
||||
next_tag_index=$((tag_index + 1))
|
||||
export TAG="$tag"
|
||||
NEXT_TAG=$(echo "$tags" | jq -r --argjson idx "$next_tag_index" '.[$idx] // empty')
|
||||
export NEXT_TAG
|
||||
|
||||
if [[ -z "$NEXT_TAG" ]]; then
|
||||
log "🔚 Skipping migration for the last tag $tag"
|
||||
continue
|
||||
fi
|
||||
|
||||
log "✨ Creating migration scripts from version $TAG to $NEXT_TAG and database $database"
|
||||
|
||||
started=1
|
||||
|
||||
# Start the database stack if not SQLite
|
||||
if [[ "$database" != "sqlite" ]]; then
|
||||
log "🚀 Starting Docker stack for $database"
|
||||
if ! docker compose -f "$database.yml" up -d; then
|
||||
log "❌ Failed to start the Docker stack for $database"
|
||||
docker compose down -v --remove-orphans
|
||||
find "$db_dir" -type d -name "__pycache__" -exec rm -rf {} +
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "🚀 Starting Docker stack for BunkerWeb"
|
||||
if ! docker compose up -d; then
|
||||
log "❌ Failed to start the Docker stack for BunkerWeb"
|
||||
docker compose down -v --remove-orphans
|
||||
find "$db_dir" -type d -name "__pycache__" -exec rm -rf {} +
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for the scheduler to be healthy
|
||||
log "⏳ Waiting for the scheduler to become healthy"
|
||||
timeout=60
|
||||
until docker compose ps bw-scheduler | grep -q "(healthy)" || [[ $timeout -le 0 ]]; do
|
||||
sleep 5
|
||||
timeout=$((timeout - 5))
|
||||
done
|
||||
|
||||
if [[ $timeout -le 0 ]]; then
|
||||
log "❌ Timeout waiting for the scheduler to be healthy"
|
||||
docker compose logs bw-scheduler
|
||||
docker compose down -v --remove-orphans
|
||||
find "$db_dir" -type d -name "__pycache__" -exec rm -rf {} +
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "✅ Scheduler is healthy"
|
||||
|
||||
docker compose stop bw-scheduler bunkerweb || true
|
||||
else
|
||||
export NEXT_TAG="$tag"
|
||||
|
||||
if [[ -z "$NEXT_TAG" ]]; then
|
||||
log "🔚 Skipping migration for the last tag $tag"
|
||||
continue
|
||||
fi
|
||||
|
||||
log "✨ Creating migration scripts from version $TAG to $NEXT_TAG and database $database"
|
||||
fi
|
||||
|
||||
transformed_tag="${NEXT_TAG//[.-]/_}.py"
|
||||
migration_dir="${db_dir}/alembic/${database}_versions"
|
||||
|
||||
# Skip if migration script already exists
|
||||
export ONLY_UPDATE=0
|
||||
if compgen -G "$migration_dir"/*_"$transformed_tag" > /dev/null; then
|
||||
log "🔄 Migration scripts for version $tag and database $database already exist"
|
||||
export ONLY_UPDATE=1
|
||||
fi
|
||||
|
||||
export DATABASE_URI="$database_uri"
|
||||
|
||||
# Run the migration script
|
||||
log "🦃 Running migration script for $tag and $database"
|
||||
if ! docker run --rm \
|
||||
--network=bw-db \
|
||||
-v bw-data:/data \
|
||||
-v bw-db:/db \
|
||||
-v bw-sqlite:/var/lib/bunkerweb \
|
||||
-v "$migration_dir":/usr/share/migration/versions \
|
||||
-e TAG \
|
||||
-e DATABASE \
|
||||
-e DATABASE_URI \
|
||||
-e NEXT_TAG \
|
||||
-e ONLY_UPDATE \
|
||||
-e UID="$(id -u)" \
|
||||
-e GID="$(id -g)" \
|
||||
local/bw-migration; then
|
||||
log "❌ Failed to run the migration script"
|
||||
docker compose down -v --remove-orphans
|
||||
find "$db_dir" -type d -name "__pycache__" -exec rm -rf {} +
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export TAG="$tag"
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
# Clean up Docker stack
|
||||
log "🧹 Cleaning up Docker stack"
|
||||
docker compose down -v --remove-orphans
|
||||
done
|
||||
|
||||
log "🎉 Migration scripts generation completed"
|
||||
|
||||
# Final cleanup
|
||||
log "🛑 Stopping and cleaning up any remaining Docker stacks"
|
||||
docker compose down -v --remove-orphans || true
|
||||
find "$db_dir" -type d -name "__pycache__" -exec rm -rf {} +
|
||||
|
||||
cd "$current_dir" || exit
|
||||
3
misc/migration/databases.json
Normal file
3
misc/migration/databases.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"postgresql": "postgresql+psycopg://bunkerweb:secret@bw-db:5432/db"
|
||||
}
|
||||
75
misc/migration/docker-compose.yml
Normal file
75
misc/migration/docker-compose.yml
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
services:
|
||||
bunkerweb:
|
||||
image: bunkerity/bunkerweb:${TAG}
|
||||
labels:
|
||||
bunkerweb.INSTANCE: "yes"
|
||||
environment:
|
||||
SERVER_NAME: ""
|
||||
API_WHITELIST_IP: "127.0.0.0/24 10.20.30.0/24"
|
||||
USE_BUNKERNET: "no"
|
||||
USE_BLACKLIST: "no"
|
||||
USE_WHITELIST: "no"
|
||||
SEND_ANONYMOUS_REPORT: "no"
|
||||
networks:
|
||||
bw-universe:
|
||||
aliases:
|
||||
- bunkerweb
|
||||
|
||||
bw-scheduler:
|
||||
image: bunkerity/bunkerweb-scheduler:${TAG}
|
||||
environment:
|
||||
BUNKERWEB_INSTANCES: "bunkerweb"
|
||||
SERVER_NAME: ""
|
||||
API_WHITELIST_IP: "127.0.0.0/24 10.20.30.0/24"
|
||||
DATABASE_URI: "${DATABASE_URI}"
|
||||
USE_BUNKERNET: "no"
|
||||
USE_BLACKLIST: "no"
|
||||
USE_WHITELIST: "no"
|
||||
SEND_ANONYMOUS_REPORT: "no"
|
||||
DOCKER_HOST: "tcp://bw-docker:2375"
|
||||
volumes:
|
||||
- bw-data:/data
|
||||
- bw-db:/usr/share/bunkerweb/db
|
||||
- bw-sqlite:/var/lib/bunkerweb
|
||||
networks:
|
||||
bw-universe:
|
||||
aliases:
|
||||
- bw-scheduler
|
||||
bw-db:
|
||||
aliases:
|
||||
- bw-scheduler
|
||||
bw-docker:
|
||||
aliases:
|
||||
- bw-scheduler
|
||||
|
||||
bw-docker:
|
||||
image: tecnativa/docker-socket-proxy:nightly
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
CONTAINERS: "1"
|
||||
LOG_LEVEL: "warning"
|
||||
networks:
|
||||
bw-docker:
|
||||
aliases:
|
||||
- bw-docker
|
||||
|
||||
volumes:
|
||||
bw-data:
|
||||
name: bw-data
|
||||
bw-db:
|
||||
name: bw-db
|
||||
bw-sqlite:
|
||||
name: bw-sqlite
|
||||
|
||||
networks:
|
||||
bw-universe:
|
||||
name: bw-universe
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.20.30.0/24
|
||||
bw-db:
|
||||
name: bw-db
|
||||
bw-docker:
|
||||
name: bw-docker
|
||||
99
misc/migration/entrypoint.sh
Normal file
99
misc/migration/entrypoint.sh
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Function for printing error messages and exiting
|
||||
function exit_with_error() {
|
||||
echo "❌ $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Function for checking required files
|
||||
function check_file_exists() {
|
||||
[ ! -f "$1" ] && exit_with_error "$1 file not found"
|
||||
}
|
||||
|
||||
# Check required files
|
||||
check_file_exists "/db/model.py"
|
||||
check_file_exists "alembic.ini"
|
||||
check_file_exists "env.py"
|
||||
check_file_exists "script.py.mako"
|
||||
|
||||
# Check required environment variables
|
||||
[ -z "$DATABASE" ] && exit_with_error "DATABASE environment variable is not set"
|
||||
[ -z "$DATABASE_URI" ] && exit_with_error "DATABASE_URI environment variable is not set"
|
||||
[ -z "$TAG" ] && exit_with_error "TAG environment variable is not set"
|
||||
[ -z "$NEXT_TAG" ] && exit_with_error "NEXT_TAG environment variable is not set"
|
||||
[ -z "$ONLY_UPDATE" ] && exit_with_error "ONLY_UPDATE environment variable is not set"
|
||||
|
||||
# Validate database type
|
||||
case "$DATABASE" in
|
||||
sqlite|mariadb|mysql|postgresql)
|
||||
;;
|
||||
*)
|
||||
exit_with_error "Unsupported database type: $DATABASE"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Configure SQLAlchemy URL in alembic.ini
|
||||
echo "🔧 Configuring SQLAlchemy URL in alembic.ini"
|
||||
sed -i "s|^sqlalchemy\\.url =.*$|sqlalchemy.url = ${DATABASE_URI}|" alembic.ini || exit_with_error "Failed to update SQLAlchemy URL in alembic.ini"
|
||||
|
||||
# Test database connection
|
||||
echo "🔗 Testing database connection..."
|
||||
python3 -c "from sqlalchemy import create_engine; create_engine('${DATABASE_URI}').connect()" || exit_with_error "Unable to connect to the database at $DATABASE_URI"
|
||||
echo "✅ Database connection successful"
|
||||
|
||||
# Download the next tag model file
|
||||
echo "📥 Downloading the model file for version $NEXT_TAG"
|
||||
if ! curl -f -s -o /db/model.py "https://raw.githubusercontent.com/bunkerity/bunkerweb/refs/tags/v${NEXT_TAG}/src/common/db/model.py"; then
|
||||
echo "⚠️ Failed to download model file, using latest_model.py instead"
|
||||
if [ -f "latest_model.py" ]; then
|
||||
mv latest_model.py /db/model.py || exit_with_error "Failed to move latest_model.py to /db/model.py"
|
||||
else
|
||||
exit_with_error "Neither model download nor latest_model.py are available"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify the downloaded file is not an error page
|
||||
if grep -q "404: Not Found" "/db/model.py"; then
|
||||
rm -f /db/model.py
|
||||
if [ -f "latest_model.py" ]; then
|
||||
mv latest_model.py /db/model.py || exit_with_error "Failed to move latest_model.py to /db/model.py"
|
||||
else
|
||||
exit_with_error "Neither model download nor latest_model.py are available"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$ONLY_UPDATE" -eq 0 ]; then
|
||||
echo "🦃 Auto-generating the migration script to upgrade from $TAG to $NEXT_TAG"
|
||||
|
||||
# Generate the migration script
|
||||
alembic revision --autogenerate -m "Upgrade to version $NEXT_TAG" --version-path versions || exit_with_error "Failed to create migration script for $DATABASE. Check alembic configuration or database connection."
|
||||
|
||||
# Set ownership for alembic directory (optional step)
|
||||
if command -v chown &>/dev/null; then
|
||||
echo "🔧 Setting ownership for alembic directory"
|
||||
chown -R "$UID:$GID" versions || echo "⚠️ Failed to change ownership, continuing..."
|
||||
else
|
||||
echo "⚠️ 'chown' command not available, skipping ownership adjustment"
|
||||
fi
|
||||
|
||||
# Apply the migration to update the database
|
||||
echo "🔄 Applying the migration..."
|
||||
alembic upgrade head || exit_with_error "Failed to apply the migration to the latest version"
|
||||
|
||||
echo "✅ Migration script created successfully"
|
||||
else
|
||||
# Apply the migration to update the database but only to the next version
|
||||
echo "🔄 Applying the migration to the next version..."
|
||||
alembic upgrade +1 || exit_with_error "Failed to apply the migration to the next version"
|
||||
|
||||
# Set ownership for alembic directory (optional step)
|
||||
if command -v chown &>/dev/null; then
|
||||
echo "🔧 Setting ownership for alembic directory"
|
||||
chown -R "$UID:$GID" versions || echo "⚠️ Failed to change ownership, continuing..."
|
||||
else
|
||||
echo "⚠️ 'chown' command not available, skipping ownership adjustment"
|
||||
fi
|
||||
|
||||
echo "✅ Successfully applied migration to the next version: $NEXT_TAG"
|
||||
fi
|
||||
16
misc/migration/mariadb.yml
Normal file
16
misc/migration/mariadb.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
services:
|
||||
bw-db:
|
||||
image: mariadb:11
|
||||
environment:
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||
MYSQL_DATABASE: "db"
|
||||
MYSQL_USER: "bunkerweb"
|
||||
MYSQL_PASSWORD: "secret"
|
||||
networks:
|
||||
bw-db:
|
||||
aliases:
|
||||
- bw-db
|
||||
|
||||
networks:
|
||||
bw-db:
|
||||
name: bw-db
|
||||
16
misc/migration/mysql.yml
Normal file
16
misc/migration/mysql.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
services:
|
||||
bw-db:
|
||||
image: mysql:8
|
||||
environment:
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||
MYSQL_DATABASE: "db"
|
||||
MYSQL_USER: "bunkerweb"
|
||||
MYSQL_PASSWORD: "secret"
|
||||
networks:
|
||||
bw-db:
|
||||
aliases:
|
||||
- bw-db
|
||||
|
||||
networks:
|
||||
bw-db:
|
||||
name: bw-db
|
||||
15
misc/migration/postgresql.yml
Normal file
15
misc/migration/postgresql.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
services:
|
||||
bw-db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: "bunkerweb"
|
||||
POSTGRES_PASSWORD: "secret"
|
||||
POSTGRES_DB: "db"
|
||||
networks:
|
||||
bw-db:
|
||||
aliases:
|
||||
- bw-db
|
||||
|
||||
networks:
|
||||
bw-db:
|
||||
name: bw-db
|
||||
File diff suppressed because it is too large
Load diff
117
src/common/db/alembic/alembic.ini
Normal file
117
src/common/db/alembic/alembic.ini
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
# Use forward slashes (/) also on windows to provide an os agnostic path
|
||||
script_location = .
|
||||
|
||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||
# Uncomment the line below if you want the files to be prepended with date and time
|
||||
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
||||
# for all available tokens
|
||||
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .,/db
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python>=3.9 or backports.zoneinfo library.
|
||||
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
||||
# string value is passed to ZoneInfo()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to alembic_mariadb/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator" below.
|
||||
version_locations = versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
||||
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
||||
# Valid values for version_path_separator are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
# version_path_separator = newline
|
||||
version_path_separator = :
|
||||
|
||||
# set to 'true' to search source files recursively
|
||||
# in each "version_locations" directory
|
||||
# new in Alembic version 1.10
|
||||
# recursive_version_locations = false
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url =
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks = black
|
||||
# black.type = console_scripts
|
||||
# black.entrypoint = black
|
||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||
|
||||
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
|
||||
# hooks = ruff
|
||||
# ruff.type = exec
|
||||
# ruff.executable = %(here)s/.venv/bin/ruff
|
||||
# ruff.options = --fix REVISION_SCRIPT_FILENAME
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARNING
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARNING
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
81
src/common/db/alembic/env.py
Normal file
81
src/common/db/alembic/env.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
from logging.config import fileConfig
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
from sqlalchemy.schema import Identity
|
||||
|
||||
from alembic import context
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
from model import Base
|
||||
|
||||
target_metadata = Base.metadata
|
||||
|
||||
|
||||
# Custom function to exclude Identity columns
|
||||
def include_object(object, name, type_, reflected, compare_to):
|
||||
if type_ == "column" and isinstance(object.server_default, Identity):
|
||||
# Exclude Identity columns
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
include_object=include_object, # Add include_object hook
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online() -> None:
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
connectable = engine_from_config(config.get_section(config.config_ini_section, {}), prefix="sqlalchemy.", poolclass=pool.NullPool)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
include_object=include_object, # Add include_object hook
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.12
|
||||
|
||||
Revision ID: 0229aafe5e96
|
||||
Revises: bf07e30a9b65
|
||||
Create Date: 2024-12-17 15:06:45.746107
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0229aafe5e96"
|
||||
down_revision: Union[str, None] = "bf07e30a9b65"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version to 1.5.12
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version to 1.5.11
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
"""Upgrade to version 1.5.8
|
||||
|
||||
Revision ID: 0949ce7a3875
|
||||
Revises: 30f52a4357a2
|
||||
Create Date: 2024-12-17 15:06:33.277221
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0949ce7a3875"
|
||||
down_revision: Union[str, None] = "30f52a4357a2"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add new columns to 'bw_metadata'
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("pro_license", sa.String(length=128), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_custom_configs_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_external_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_pro_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_instances_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("failover", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Add new columns to 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_config_change", sa.DateTime(), nullable=True))
|
||||
|
||||
# Set default value for 'config_changed' in 'bw_plugins'
|
||||
op.execute("UPDATE bw_plugins SET config_changed = false")
|
||||
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert 'bw_plugins' changes
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("last_config_change")
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Revert 'bw_metadata' changes
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("failover")
|
||||
batch_op.drop_column("last_instances_change")
|
||||
batch_op.drop_column("last_pro_plugins_change")
|
||||
batch_op.drop_column("last_external_plugins_change")
|
||||
batch_op.drop_column("last_custom_configs_change")
|
||||
batch_op.drop_column("pro_license")
|
||||
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7', config_changed = false WHERE id = 1")
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
"""Upgrade to version 1.5.6
|
||||
|
||||
Revision ID: 18e9d2191dcc
|
||||
Revises: d4d8df48d14d
|
||||
Create Date: 2024-12-17 10:19:55.586854
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "18e9d2191dcc"
|
||||
down_revision: Union[str, None] = "d4d8df48d14d"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Drop the foreign key constraint with the correct name
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_ibfk_1", type_="foreignkey") # Replace with actual name
|
||||
batch_op.drop_index("job_name")
|
||||
batch_op.create_foreign_key("fk_bw_jobs_cache_job_name", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
# Other migration operations
|
||||
op.add_column("bw_metadata", sa.Column("is_pro", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_metadata", sa.Column("pro_expire", sa.DateTime(), nullable=True))
|
||||
op.add_column(
|
||||
"bw_metadata",
|
||||
sa.Column("pro_status", sa.Enum("active", "invalid", "expired", "suspended", name="pro_status_enum"), nullable=False, server_default="invalid"),
|
||||
)
|
||||
op.add_column("bw_metadata", sa.Column("pro_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
op.add_column("bw_metadata", sa.Column("pro_overlapped", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_metadata", sa.Column("last_pro_check", sa.DateTime(), nullable=True))
|
||||
op.add_column("bw_metadata", sa.Column("pro_plugins_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
op.add_column("bw_services", sa.Column("is_draft", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
|
||||
op.add_column("bw_plugins", sa.Column("type", sa.Enum("core", "external", "pro", name="plugin_types_enum"), nullable=False, server_default="core"))
|
||||
op.alter_column(
|
||||
"bw_plugins", "stream", existing_type=mysql.VARCHAR(length=16), type_=sa.Enum("no", "yes", "partial", name="stream_types_enum"), existing_nullable=False
|
||||
)
|
||||
|
||||
# Migrate data: Set 'type' to 'external' where 'external' was true
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET type = 'external'
|
||||
WHERE external = true
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_column("bw_plugins", "external")
|
||||
|
||||
op.alter_column("bw_global_values", "value", existing_type=mysql.VARCHAR(length=8192), type_=sa.TEXT(), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=mysql.VARCHAR(length=8192), type_=sa.TEXT(), existing_nullable=False)
|
||||
|
||||
op.drop_index("name", table_name="bw_jobs")
|
||||
op.drop_index("name", table_name="bw_settings")
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET is_pro = false,
|
||||
pro_status = 'invalid',
|
||||
pro_services = 0,
|
||||
pro_overlapped = false,
|
||||
pro_plugins_changed = false,
|
||||
version = '1.5.6'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("fk_bw_jobs_cache_job_name", type_="foreignkey")
|
||||
batch_op.create_index("job_name", ["job_name", "service_id", "file_name"], unique=True)
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_1", "bw_jobs", ["job_name"], ["name"]) # Replace with actual name
|
||||
|
||||
op.create_index("name", "bw_jobs", ["name", "plugin_id"], unique=True)
|
||||
op.create_index("name", "bw_settings", ["name"], unique=True)
|
||||
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.TEXT(), type_=mysql.VARCHAR(length=8192), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.TEXT(), type_=mysql.VARCHAR(length=8192), existing_nullable=False)
|
||||
|
||||
op.add_column("bw_plugins", sa.Column("external", mysql.TINYINT(display_width=1), autoincrement=False, nullable=False))
|
||||
|
||||
# Migrate data: Set 'external' to true where 'type' was 'external'
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET external = true
|
||||
WHERE type = 'external'
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_column("bw_plugins", "type")
|
||||
op.alter_column(
|
||||
"bw_plugins", "stream", existing_type=sa.Enum("no", "yes", "partial", name="stream_types_enum"), type_=mysql.VARCHAR(length=16), existing_nullable=False
|
||||
)
|
||||
|
||||
op.drop_column("bw_services", "is_draft")
|
||||
|
||||
op.drop_column("bw_metadata", "pro_plugins_changed")
|
||||
op.drop_column("bw_metadata", "last_pro_check")
|
||||
op.drop_column("bw_metadata", "pro_overlapped")
|
||||
op.drop_column("bw_metadata", "pro_services")
|
||||
op.drop_column("bw_metadata", "pro_status")
|
||||
op.drop_column("bw_metadata", "pro_expire")
|
||||
op.drop_column("bw_metadata", "is_pro")
|
||||
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
"""Upgrade to version 1.5.4
|
||||
|
||||
Revision ID: 1cc06aa8335c
|
||||
Revises: c38183e63472
|
||||
Create Date: 2024-12-17 10:19:48.503973
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "1cc06aa8335c"
|
||||
down_revision: Union[str, None] = "c38183e63472"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create new table 'bw_ui_users'
|
||||
op.create_table(
|
||||
"bw_ui_users",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("username", sa.String(length=256), nullable=False),
|
||||
sa.Column("password", sa.String(length=60), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("username"),
|
||||
)
|
||||
|
||||
# Update metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop the table 'bw_ui_users'
|
||||
op.drop_table("bw_ui_users")
|
||||
|
||||
# Revert metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.2
|
||||
|
||||
Revision ID: 24e08b364fa1
|
||||
Revises: cc61497f1976
|
||||
Create Date: 2024-12-17 10:19:39.901086
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "24e08b364fa1"
|
||||
down_revision: Union[str, None] = "cc61497f1976"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.1
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.1' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
"""Upgrade to version 1.5.7
|
||||
|
||||
Revision ID: 30f52a4357a2
|
||||
Revises: 18e9d2191dcc
|
||||
Create Date: 2024-12-17 10:19:55.586854
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "30f52a4357a2"
|
||||
down_revision: Union[str, None] = "18e9d2191dcc"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create new table for bw_cli_commands
|
||||
op.create_table(
|
||||
"bw_cli_commands",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(always=False, start=1, increment=1), nullable=False),
|
||||
sa.Column("name", sa.String(length=64), nullable=False),
|
||||
sa.Column("plugin_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("file_name", sa.String(length=256), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="cascade", ondelete="cascade"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("plugin_id", "name"),
|
||||
)
|
||||
|
||||
# Handle foreign key constraints for bw_jobs_cache
|
||||
op.drop_constraint("fk_bw_jobs_cache_job_name", "bw_jobs_cache", type_="foreignkey")
|
||||
op.create_foreign_key(None, "bw_jobs_cache", "bw_jobs", ["job_name"], ["name"], onupdate="cascade", ondelete="cascade")
|
||||
|
||||
# Add new columns to bw_plugin_pages
|
||||
op.add_column(
|
||||
"bw_plugin_pages",
|
||||
sa.Column("obfuscation_file", mysql.LONGBLOB(), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"bw_plugin_pages",
|
||||
sa.Column("obfuscation_checksum", sa.String(length=128), nullable=True),
|
||||
)
|
||||
|
||||
# Add the new order column to bw_settings
|
||||
op.add_column("bw_settings", sa.Column("order", sa.Integer(), nullable=False))
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the version update
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.6' WHERE id = 1")
|
||||
|
||||
# Reverse the addition of the order column
|
||||
op.drop_column("bw_settings", "order")
|
||||
|
||||
# Reverse changes in bw_plugin_pages
|
||||
op.drop_column("bw_plugin_pages", "obfuscation_checksum")
|
||||
op.drop_column("bw_plugin_pages", "obfuscation_file")
|
||||
|
||||
# Restore foreign key constraints for bw_jobs_cache
|
||||
op.drop_constraint(None, "bw_jobs_cache", type_="foreignkey")
|
||||
op.create_foreign_key("fk_bw_jobs_cache_job_name", "bw_jobs_cache", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
# Drop the newly created table bw_cli_commands
|
||||
op.drop_table("bw_cli_commands")
|
||||
|
||||
# Revert the version update
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.6' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.9
|
||||
|
||||
Revision ID: 392ec43997fd
|
||||
Revises: 0949ce7a3875
|
||||
Create Date: 2024-12-17 15:06:36.162645
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "392ec43997fd"
|
||||
down_revision: Union[str, None] = "0949ce7a3875"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.10
|
||||
|
||||
Revision ID: 431f4dea783e
|
||||
Revises: 392ec43997fd
|
||||
Create Date: 2024-12-17 15:06:39.416494
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "431f4dea783e"
|
||||
down_revision: Union[str, None] = "392ec43997fd"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata' back to 1.5.9
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
"""Upgrade to version 1.6.0-rc1
|
||||
|
||||
Revision ID: 8a37fed772e9
|
||||
Revises: bfa7869e34c3
|
||||
Create Date: 2024-12-20 10:19:30.469285
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "8a37fed772e9"
|
||||
down_revision: Union[str, None] = "bfa7869e34c3"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Drop foreign keys referencing service_id columns before altering them
|
||||
# Replace these FK names with the actual names in your schema
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.drop_constraint("bw_custom_configs_ibfk_1", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_ibfk_2", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.drop_constraint("bw_services_settings_ibfk_1", type_="foreignkey")
|
||||
|
||||
# Alter columns now that foreign keys are dropped
|
||||
op.alter_column("bw_custom_configs", "service_id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=True)
|
||||
|
||||
op.alter_column("bw_jobs_cache", "service_id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=True)
|
||||
|
||||
op.alter_column("bw_services", "id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_services_settings", "service_id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=False)
|
||||
|
||||
# After altering, recreate the foreign keys with updated references
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.create_foreign_key("bw_custom_configs_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_2", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.create_foreign_key("bw_services_settings_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
# Update bw_settings.default from String(4096) to TEXT
|
||||
op.alter_column("bw_settings", "default", existing_type=mysql.VARCHAR(length=4096), type_=sa.TEXT(), existing_nullable=True)
|
||||
|
||||
# Drop bw_ui_users.id column
|
||||
op.drop_column("bw_ui_users", "id")
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-rc1' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the changes:
|
||||
# 1. Drop foreign keys
|
||||
# 2. Revert column types
|
||||
# 3. Recreate foreign keys
|
||||
# 4. Revert version
|
||||
# 5. Re-add bw_ui_users.id column
|
||||
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.drop_constraint("bw_custom_configs_ibfk_1", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_ibfk_1", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.drop_constraint("bw_services_settings_ibfk_1", type_="foreignkey")
|
||||
|
||||
op.alter_column("bw_services_settings", "service_id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_services", "id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_jobs_cache", "service_id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=True)
|
||||
|
||||
op.alter_column("bw_custom_configs", "service_id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=True)
|
||||
|
||||
# Recreate foreign keys with old definitions
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.create_foreign_key("bw_custom_configs_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.create_foreign_key("bw_services_settings_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
op.alter_column("bw_settings", "default", existing_type=sa.TEXT(), type_=mysql.VARCHAR(length=4096), existing_nullable=True)
|
||||
|
||||
# Re-add bw_ui_users.id and index on username
|
||||
op.add_column("bw_ui_users", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False))
|
||||
|
||||
# Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
"""Upgrade to version 1.5.0
|
||||
|
||||
Revision ID: b46c7ecfba26
|
||||
Revises:
|
||||
Create Date: 2024-12-17 10:19:30.419613
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "b46c7ecfba26"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Step 1: Drop 'order' column from 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("order")
|
||||
|
||||
# Data migration: Update the version to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Re-add the 'order' column to the 'bw_plugins' table
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("order", mysql.INTEGER(display_width=11), autoincrement=False, nullable=False))
|
||||
|
||||
# Data migration: Update the version to 1.5.0-beta
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
"""Upgrade to version 1.5.11
|
||||
|
||||
Revision ID: bf07e30a9b65
|
||||
Revises: 431f4dea783e
|
||||
Create Date: 2024-12-17 15:06:42.631670
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "bf07e30a9b65"
|
||||
down_revision: Union[str, None] = "431f4dea783e"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add the new non_draft_services column to bw_metadata
|
||||
op.add_column("bw_metadata", sa.Column("non_draft_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
|
||||
# Update the version in metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the addition of the non_draft_services column
|
||||
op.drop_column("bw_metadata", "non_draft_services")
|
||||
|
||||
# Revert version to 1.5.10
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
"""Upgrade to version 1.6.0-beta
|
||||
|
||||
Revision ID: bfa7869e34c3
|
||||
Revises: 0229aafe5e96
|
||||
Create Date: 2024-12-17 15:06:49.235416
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "bfa7869e34c3"
|
||||
down_revision: Union[str, None] = "0229aafe5e96"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Define old and new ENUMs for methods_enum (adding "wizard")
|
||||
old_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
new_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum")
|
||||
|
||||
# Alter columns that use methods_enum
|
||||
op.alter_column("bw_plugins", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False, existing_server_default="manual")
|
||||
|
||||
op.alter_column("bw_services", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_custom_configs", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_ui_users", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False, existing_server_default="manual")
|
||||
|
||||
# Update custom_configs_types_enum (adding "default_server_stream", "crs_plugins_before", "crs_plugins_after")
|
||||
old_cct_enum = sa.Enum("http", "stream", "server_http", "server_stream", "default_server_http", "modsec", "modsec_crs", name="custom_configs_types_enum")
|
||||
new_cct_enum = sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
)
|
||||
|
||||
op.alter_column("bw_custom_configs", "type", existing_type=old_cct_enum, type_=new_cct_enum, existing_nullable=False)
|
||||
|
||||
# Update plugin_types_enum (adding "ui")
|
||||
old_pt_enum = sa.Enum("core", "external", "pro", name="plugin_types_enum")
|
||||
new_pt_enum = sa.Enum("core", "external", "ui", "pro", name="plugin_types_enum")
|
||||
|
||||
op.alter_column("bw_plugins", "type", existing_type=old_pt_enum, type_=new_pt_enum, existing_nullable=False, existing_server_default="core")
|
||||
|
||||
# Create new UI tables
|
||||
op.create_table(
|
||||
"bw_ui_permissions",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
sa.Column("description", sa.String(256), nullable=False),
|
||||
sa.Column("update_datetime", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_recovery_codes",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("code", sa.UnicodeText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_permissions",
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.Column("permission_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["permission_name"], ["bw_ui_permissions.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("role_name", "permission_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_users",
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("user_name", "role_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_sessions",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("ip", sa.String(39), nullable=False),
|
||||
sa.Column("user_agent", sa.TEXT, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_activity", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
from model import JSONText
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_columns_preferences",
|
||||
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("table_name", sa.Enum("bans", "configs", "instances", "jobs", "plugins", "reports", "services", name="tables_enum"), nullable=False),
|
||||
sa.Column("columns", JSONText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("user_name", "table_name", name="uq_user_columns_preferences"),
|
||||
)
|
||||
|
||||
# Templates tables
|
||||
op.create_table(
|
||||
"bw_templates",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("name", sa.String(256), unique=True, nullable=False),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_steps",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False, primary_key=True),
|
||||
sa.Column("title", sa.TEXT, nullable=False),
|
||||
sa.Column("subtitle", sa.TEXT, nullable=True),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id", "template_id"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_settings",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("default", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer(), nullable=True, default=0),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["setting_id"], ["bw_settings.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "setting_id", "step_id", "suffix"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_custom_configs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("type", new_cct_enum, nullable=False),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "step_id", "type", "name"),
|
||||
)
|
||||
|
||||
# bw_jobs changes (add run_async, remove success and last_run)
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.add_column(sa.Column("run_async", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.drop_column("success")
|
||||
batch_op.drop_column("last_run")
|
||||
|
||||
op.create_table(
|
||||
"bw_jobs_runs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("success", sa.Boolean(), nullable=True, server_default="0"),
|
||||
sa.Column("start_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("end_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["job_name"], ["bw_jobs.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
# bw_services add creation_date, last_update
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_update", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
# Set current timestamp for existing services data
|
||||
op.execute("UPDATE bw_services SET creation_date = CURRENT_TIMESTAMP, last_update = CURRENT_TIMESTAMP")
|
||||
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.alter_column("creation_date", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
batch_op.alter_column("last_update", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
|
||||
# bw_ui_users changes
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.add_column(sa.Column("email", sa.String(256), nullable=True, unique=True))
|
||||
batch_op.add_column(sa.Column("admin", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.add_column(sa.Column("theme", sa.Enum("light", "dark", name="themes_enum"), nullable=False, server_default="light"))
|
||||
batch_op.add_column(sa.Column("totp_secret", sa.String(256), nullable=True))
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp()))
|
||||
batch_op.add_column(sa.Column("update_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp()))
|
||||
batch_op.drop_column("is_two_factor_enabled")
|
||||
batch_op.drop_column("secret_token")
|
||||
|
||||
# Set defaults for existing bw_ui_users rows
|
||||
op.execute("UPDATE bw_ui_users SET admin=1, theme='light', creation_date=CURRENT_TIMESTAMP, update_date=CURRENT_TIMESTAMP")
|
||||
|
||||
# bw_plugin_pages changes
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.add_column(sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("checksum", sa.String(128), nullable=False))
|
||||
|
||||
# Add old data to new columns
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugin_pages
|
||||
SET data = template_file, checksum = template_checksum
|
||||
WHERE template_file IS NOT NULL
|
||||
"""
|
||||
)
|
||||
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.drop_column("template_file")
|
||||
batch_op.drop_column("template_checksum")
|
||||
batch_op.drop_column("actions_file")
|
||||
batch_op.drop_column("actions_checksum")
|
||||
batch_op.drop_column("obfuscation_file")
|
||||
batch_op.drop_column("obfuscation_checksum")
|
||||
|
||||
# bw_instances changes
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.add_column(sa.Column("name", sa.String(256), nullable=True))
|
||||
# Add the 'type' column with the old enum definition (or no enum yet if it didn’t exist before)
|
||||
# For instance, if previously there was no enum, start with the initial values:
|
||||
old_it_enum = sa.Enum("static", name="instance_type_enum") # The original value set
|
||||
batch_op.add_column(sa.Column("type", old_it_enum, nullable=True))
|
||||
# Similarly add the 'status', 'method', 'creation_date', 'last_seen' columns
|
||||
old_is_enum = sa.Enum("loading", "up", name="instance_status_enum")
|
||||
batch_op.add_column(sa.Column("status", old_is_enum, nullable=True))
|
||||
|
||||
old_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
batch_op.add_column(sa.Column("method", old_methods_enum, nullable=True))
|
||||
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_seen", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
op.execute("DELETE FROM bw_instances WHERE name IS NULL")
|
||||
|
||||
# Now that columns exist and have values, alter their definitions if needed:
|
||||
new_it_enum = sa.Enum("static", "container", "pod", name="instance_type_enum")
|
||||
op.alter_column("bw_instances", "type", existing_type=old_it_enum, type_=new_it_enum, existing_nullable=False, existing_server_default="static")
|
||||
|
||||
new_is_enum = sa.Enum("loading", "up", "down", name="instance_status_enum")
|
||||
op.alter_column("bw_instances", "status", existing_type=old_is_enum, type_=new_is_enum, existing_nullable=False, existing_server_default="loading")
|
||||
|
||||
# Similarly alter the method column to the new enum with 'wizard' if needed:
|
||||
extended_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum")
|
||||
op.alter_column(
|
||||
"bw_instances", "method", existing_type=old_methods_enum, type_=extended_methods_enum, existing_nullable=False, existing_server_default="manual"
|
||||
)
|
||||
|
||||
# Finally, now set them to NOT NULL
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.alter_column("name", existing_type=sa.String(256), nullable=False)
|
||||
batch_op.alter_column("type", existing_type=new_it_enum, nullable=False)
|
||||
batch_op.alter_column("status", existing_type=new_is_enum, nullable=False)
|
||||
batch_op.alter_column("method", existing_type=extended_methods_enum, nullable=False)
|
||||
batch_op.alter_column("creation_date", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
batch_op.alter_column("last_seen", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
|
||||
# Update version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert enums to their old definitions by altering columns back
|
||||
old_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
# methods_enum had "wizard" added, now remove it
|
||||
op.alter_column(
|
||||
"bw_plugins",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="manual",
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_services",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_custom_configs",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_ui_users",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="manual",
|
||||
)
|
||||
|
||||
# custom_configs_types_enum remove "default_server_stream", "crs_plugins_before", "crs_plugins_after"
|
||||
old_cct_enum = sa.Enum("http", "default_server_http", "server_http", "modsec", "modsec_crs", "stream", "server_stream", name="custom_configs_types_enum")
|
||||
op.alter_column(
|
||||
"bw_custom_configs",
|
||||
"type",
|
||||
existing_type=sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
type_=old_cct_enum,
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
# plugin_types_enum remove "ui"
|
||||
old_pt_enum = sa.Enum("core", "external", "pro", name="plugin_types_enum")
|
||||
op.alter_column(
|
||||
"bw_plugins",
|
||||
"type",
|
||||
existing_type=sa.Enum("core", "external", "ui", "pro", name="plugin_types_enum"),
|
||||
type_=old_pt_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="core",
|
||||
)
|
||||
|
||||
# instance_type_enum revert to just "static"
|
||||
old_it_enum = sa.Enum("static", name="instance_type_enum")
|
||||
op.alter_column(
|
||||
"bw_instances",
|
||||
"type",
|
||||
existing_type=sa.Enum("static", "container", "pod", name="instance_type_enum"),
|
||||
type_=old_it_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="static",
|
||||
)
|
||||
|
||||
# instance_status_enum revert to "loading", "up"
|
||||
old_is_enum = sa.Enum("loading", "up", name="instance_status_enum")
|
||||
op.alter_column(
|
||||
"bw_instances",
|
||||
"status",
|
||||
existing_type=sa.Enum("loading", "up", "down", name="instance_status_enum"),
|
||||
type_=old_is_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="loading",
|
||||
)
|
||||
|
||||
# Drop newly created UI and templates tables
|
||||
op.drop_table("bw_template_custom_configs")
|
||||
op.drop_table("bw_template_settings")
|
||||
op.drop_table("bw_template_steps")
|
||||
op.drop_table("bw_templates")
|
||||
op.drop_table("bw_ui_user_columns_preferences")
|
||||
op.drop_table("bw_ui_user_sessions")
|
||||
op.drop_table("bw_ui_roles_users")
|
||||
op.drop_table("bw_ui_roles_permissions")
|
||||
op.drop_table("bw_ui_user_recovery_codes")
|
||||
op.drop_table("bw_ui_roles")
|
||||
op.drop_table("bw_ui_permissions")
|
||||
|
||||
# Revert bw_settings constraints: old primary key was (id,name) and there was a unique constraint on id
|
||||
op.drop_constraint("bw_settings_name_key", "bw_settings", type_="unique")
|
||||
op.drop_constraint("bw_settings_pkey", "bw_settings", type_="primary")
|
||||
|
||||
# Recreate old constraints:
|
||||
# PrimaryKeyConstraint("id", "name"), UniqueConstraint("id")
|
||||
# Note: The original primary key and unique constraints must be re-added as they were initially.
|
||||
op.create_primary_key("bw_settings_pkey", "bw_settings", ["id", "name"])
|
||||
op.create_unique_constraint("id", "bw_settings", ["id"])
|
||||
|
||||
# bw_jobs revert: drop run_async, add success and last_run
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.drop_column("run_async")
|
||||
batch_op.add_column(sa.Column("success", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_run", sa.DateTime(), nullable=True))
|
||||
|
||||
# Drop bw_jobs_runs table
|
||||
op.drop_table("bw_jobs_runs")
|
||||
|
||||
# bw_services revert: remove creation_date, last_update
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.drop_column("last_update")
|
||||
batch_op.drop_column("creation_date")
|
||||
|
||||
# bw_ui_users revert: drop email, admin, theme, totp_secret, creation_date, update_date
|
||||
# add back is_two_factor_enabled, secret_token
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.drop_column("email")
|
||||
batch_op.drop_column("admin")
|
||||
batch_op.drop_column("theme")
|
||||
batch_op.drop_column("totp_secret")
|
||||
batch_op.drop_column("creation_date")
|
||||
batch_op.drop_column("update_date")
|
||||
batch_op.add_column(sa.Column("is_two_factor_enabled", sa.Boolean, nullable=False, server_default="0"))
|
||||
batch_op.add_column(sa.Column("secret_token", sa.String(32), nullable=True, unique=True, default=None))
|
||||
|
||||
# bw_plugin_pages revert: remove data, checksum, add template_file, template_checksum, actions_file, actions_checksum, obfuscation_file, obfuscation_checksum
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.drop_column("data")
|
||||
batch_op.drop_column("checksum")
|
||||
batch_op.add_column(sa.Column("template_file", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("template_checksum", sa.String(128), nullable=False))
|
||||
batch_op.add_column(sa.Column("actions_file", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("actions_checksum", sa.String(128), nullable=False))
|
||||
batch_op.add_column(sa.Column("obfuscation_file", sa.LargeBinary(length=(2**32) - 1), nullable=True))
|
||||
batch_op.add_column(sa.Column("obfuscation_checksum", sa.String(128), nullable=True))
|
||||
|
||||
# bw_instances revert: drop name, type, status, method, creation_date, last_seen
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.drop_column("last_seen")
|
||||
batch_op.drop_column("creation_date")
|
||||
batch_op.drop_column("method")
|
||||
batch_op.drop_column("status")
|
||||
batch_op.drop_column("type")
|
||||
batch_op.drop_column("name")
|
||||
|
||||
# Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.3
|
||||
|
||||
Revision ID: c38183e63472
|
||||
Revises: 24e08b364fa1
|
||||
Create Date: 2024-12-17 10:19:43.986783
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "c38183e63472"
|
||||
down_revision: Union[str, None] = "24e08b364fa1"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.3
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Upgrade to version 1.5.1
|
||||
|
||||
Revision ID: cc61497f1976
|
||||
Revises: b46c7ecfba26
|
||||
Create Date: 2024-12-17 10:19:35.186344
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "cc61497f1976"
|
||||
down_revision: Union[str, None] = "b46c7ecfba26"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Add new columns to bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("scheduler_first_start", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("custom_configs_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("external_plugins_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("instances_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET scheduler_first_start = false,
|
||||
custom_configs_changed = false,
|
||||
external_plugins_changed = false,
|
||||
config_changed = false,
|
||||
instances_changed = false,
|
||||
version = '1.5.1'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Remove new columns from bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.drop_column("instances_changed")
|
||||
batch_op.drop_column("config_changed")
|
||||
batch_op.drop_column("external_plugins_changed")
|
||||
batch_op.drop_column("custom_configs_changed")
|
||||
batch_op.drop_column("scheduler_first_start")
|
||||
|
||||
# Revert the version back to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
"""Upgrade to version 1.5.5
|
||||
|
||||
Revision ID: d4d8df48d14d
|
||||
Revises: 1cc06aa8335c
|
||||
Create Date: 2024-12-17 10:19:52.815063
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "d4d8df48d14d"
|
||||
down_revision: Union[str, None] = "1cc06aa8335c"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add new columns to bw_ui_users
|
||||
op.add_column("bw_ui_users", sa.Column("is_two_factor_enabled", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_ui_users", sa.Column("secret_token", sa.String(length=32), nullable=True))
|
||||
op.add_column(
|
||||
"bw_ui_users", sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum"), nullable=False, server_default="manual")
|
||||
)
|
||||
op.create_unique_constraint("uq_bw_ui_users_secret_token", "bw_ui_users", ["secret_token"])
|
||||
|
||||
# Increase column sizes
|
||||
op.alter_column("bw_global_values", "value", existing_type=mysql.VARCHAR(length=4096), type_=sa.String(length=8192), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=mysql.VARCHAR(length=4096), type_=sa.String(length=8192), existing_nullable=False)
|
||||
|
||||
# Update all new columns in a single statement
|
||||
op.execute("UPDATE bw_ui_users SET is_two_factor_enabled = false, method = 'manual'")
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert changes to 'bw_ui_users'
|
||||
op.drop_constraint("uq_bw_ui_users_secret_token", "bw_ui_users", type_="unique")
|
||||
op.drop_column("bw_ui_users", "method")
|
||||
op.drop_column("bw_ui_users", "secret_token")
|
||||
op.drop_column("bw_ui_users", "is_two_factor_enabled")
|
||||
|
||||
# Revert column sizes for VARCHAR
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.String(length=8192), type_=mysql.VARCHAR(length=4096), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.String(length=8192), type_=mysql.VARCHAR(length=4096), existing_nullable=False)
|
||||
|
||||
# Revert version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
"""Upgrade to version 1.5.6
|
||||
|
||||
Revision ID: 021e3123e517
|
||||
Revises: 0903238e095e
|
||||
Create Date: 2024-12-19 13:23:02.905461
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "021e3123e517"
|
||||
down_revision: Union[str, None] = "0903238e095e"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Drop the foreign key constraint with the correct name
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_ibfk_1", type_="foreignkey") # Replace with actual name
|
||||
batch_op.drop_index("job_name")
|
||||
batch_op.create_foreign_key("fk_bw_jobs_cache_job_name", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
# Other migration operations
|
||||
op.add_column("bw_metadata", sa.Column("is_pro", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_metadata", sa.Column("pro_expire", sa.DateTime(), nullable=True))
|
||||
op.add_column(
|
||||
"bw_metadata",
|
||||
sa.Column("pro_status", sa.Enum("active", "invalid", "expired", "suspended", name="pro_status_enum"), nullable=False, server_default="invalid"),
|
||||
)
|
||||
op.add_column("bw_metadata", sa.Column("pro_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
op.add_column("bw_metadata", sa.Column("pro_overlapped", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_metadata", sa.Column("last_pro_check", sa.DateTime(), nullable=True))
|
||||
op.add_column("bw_metadata", sa.Column("pro_plugins_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
op.add_column("bw_services", sa.Column("is_draft", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
|
||||
op.add_column("bw_plugins", sa.Column("type", sa.Enum("core", "external", "pro", name="plugin_types_enum"), nullable=False, server_default="core"))
|
||||
op.alter_column(
|
||||
"bw_plugins", "stream", existing_type=mysql.VARCHAR(length=16), type_=sa.Enum("no", "yes", "partial", name="stream_types_enum"), existing_nullable=False
|
||||
)
|
||||
|
||||
# Migrate data: Set 'type' to 'external' where 'external' was true
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET type = 'external'
|
||||
WHERE external = true
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_column("bw_plugins", "external")
|
||||
|
||||
op.alter_column("bw_global_values", "value", existing_type=mysql.VARCHAR(length=8192), type_=sa.TEXT(), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=mysql.VARCHAR(length=8192), type_=sa.TEXT(), existing_nullable=False)
|
||||
|
||||
op.drop_index("name", table_name="bw_jobs")
|
||||
op.drop_index("name", table_name="bw_settings")
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET is_pro = false,
|
||||
pro_status = 'invalid',
|
||||
pro_services = 0,
|
||||
pro_overlapped = false,
|
||||
pro_plugins_changed = false,
|
||||
version = '1.5.6'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("fk_bw_jobs_cache_job_name", type_="foreignkey")
|
||||
batch_op.create_index("job_name", ["job_name", "service_id", "file_name"], unique=True)
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_1", "bw_jobs", ["job_name"], ["name"]) # Replace with actual name
|
||||
|
||||
op.create_index("name", "bw_jobs", ["name", "plugin_id"], unique=True)
|
||||
op.create_index("name", "bw_settings", ["name"], unique=True)
|
||||
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.TEXT(), type_=mysql.VARCHAR(length=8192), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.TEXT(), type_=mysql.VARCHAR(length=8192), existing_nullable=False)
|
||||
|
||||
op.add_column("bw_plugins", sa.Column("external", mysql.TINYINT(display_width=1), autoincrement=False, nullable=False))
|
||||
|
||||
# Migrate data: Set 'external' to true where 'type' was 'external'
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET external = true
|
||||
WHERE type = 'external'
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_column("bw_plugins", "type")
|
||||
op.alter_column(
|
||||
"bw_plugins", "stream", existing_type=sa.Enum("no", "yes", "partial", name="stream_types_enum"), type_=mysql.VARCHAR(length=16), existing_nullable=False
|
||||
)
|
||||
|
||||
op.drop_column("bw_services", "is_draft")
|
||||
|
||||
op.drop_column("bw_metadata", "pro_plugins_changed")
|
||||
op.drop_column("bw_metadata", "last_pro_check")
|
||||
op.drop_column("bw_metadata", "pro_overlapped")
|
||||
op.drop_column("bw_metadata", "pro_services")
|
||||
op.drop_column("bw_metadata", "pro_status")
|
||||
op.drop_column("bw_metadata", "pro_expire")
|
||||
op.drop_column("bw_metadata", "is_pro")
|
||||
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
"""Upgrade to version 1.5.5
|
||||
|
||||
Revision ID: 0903238e095e
|
||||
Revises: 3f152772560d
|
||||
Create Date: 2024-12-19 13:21:42.890471
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0903238e095e"
|
||||
down_revision: Union[str, None] = "3f152772560d"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add new columns to bw_ui_users
|
||||
op.add_column("bw_ui_users", sa.Column("is_two_factor_enabled", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_ui_users", sa.Column("secret_token", sa.String(length=32), nullable=True))
|
||||
op.add_column(
|
||||
"bw_ui_users", sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum"), nullable=False, server_default="manual")
|
||||
)
|
||||
op.create_unique_constraint("uq_bw_ui_users_secret_token", "bw_ui_users", ["secret_token"])
|
||||
|
||||
# Increase column sizes
|
||||
op.alter_column("bw_global_values", "value", existing_type=mysql.VARCHAR(length=4096), type_=sa.String(length=8192), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=mysql.VARCHAR(length=4096), type_=sa.String(length=8192), existing_nullable=False)
|
||||
|
||||
# Update all new columns in a single statement
|
||||
op.execute("UPDATE bw_ui_users SET is_two_factor_enabled = false, method = 'manual'")
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert changes to 'bw_ui_users'
|
||||
op.drop_constraint("uq_bw_ui_users_secret_token", "bw_ui_users", type_="unique")
|
||||
op.drop_column("bw_ui_users", "method")
|
||||
op.drop_column("bw_ui_users", "secret_token")
|
||||
op.drop_column("bw_ui_users", "is_two_factor_enabled")
|
||||
|
||||
# Revert column sizes for VARCHAR
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.String(length=8192), type_=mysql.VARCHAR(length=4096), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.String(length=8192), type_=mysql.VARCHAR(length=4096), existing_nullable=False)
|
||||
|
||||
# Revert version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
"""Upgrade to version 1.5.11
|
||||
|
||||
Revision ID: 12ffcd2b9d63
|
||||
Revises: b03a64d4d34a
|
||||
Create Date: 2024-12-19 13:30:48.808893
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "12ffcd2b9d63"
|
||||
down_revision: Union[str, None] = "b03a64d4d34a"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add the new non_draft_services column to bw_metadata
|
||||
op.add_column("bw_metadata", sa.Column("non_draft_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
|
||||
# Update the version in metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the addition of the non_draft_services column
|
||||
op.drop_column("bw_metadata", "non_draft_services")
|
||||
|
||||
# Revert version to 1.5.10
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.12
|
||||
|
||||
Revision ID: 3d6af0bf1bec
|
||||
Revises: 12ffcd2b9d63
|
||||
Create Date: 2024-12-19 13:32:27.783961
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "3d6af0bf1bec"
|
||||
down_revision: Union[str, None] = "12ffcd2b9d63"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version to 1.5.12
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version to 1.5.11
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
"""Upgrade to version 1.5.4
|
||||
|
||||
Revision ID: 3f152772560d
|
||||
Revises: e65b18370d91
|
||||
Create Date: 2024-12-19 13:20:01.915208
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "3f152772560d"
|
||||
down_revision: Union[str, None] = "e65b18370d91"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create new table 'bw_ui_users'
|
||||
op.create_table(
|
||||
"bw_ui_users",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("username", sa.String(length=256), nullable=False),
|
||||
sa.Column("password", sa.String(length=60), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("username"),
|
||||
)
|
||||
|
||||
# Update metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop the table 'bw_ui_users'
|
||||
op.drop_table("bw_ui_users")
|
||||
|
||||
# Revert metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
"""Upgrade to version 1.5.0
|
||||
|
||||
Revision ID: 41d395e48cfd
|
||||
Revises:
|
||||
Create Date: 2024-12-19 13:15:17.279882
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "41d395e48cfd"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Step 1: Drop 'order' column from 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("order")
|
||||
|
||||
# Data migration: Update the version to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Re-add the 'order' column to the 'bw_plugins' table
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("order", mysql.INTEGER(display_width=11), autoincrement=False, nullable=False))
|
||||
|
||||
# Data migration: Update the version to 1.5.0-beta
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
"""Upgrade to version 1.6.0-rc1
|
||||
|
||||
Revision ID: 6307fa627563
|
||||
Revises: 839424d81cf7
|
||||
Create Date: 2024-12-20 10:41:52.149076
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "6307fa627563"
|
||||
down_revision: Union[str, None] = "839424d81cf7"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Drop foreign keys referencing service_id columns before altering them
|
||||
# Replace these FK names with the actual names in your schema
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.drop_constraint("bw_custom_configs_ibfk_1", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_ibfk_2", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.drop_constraint("bw_services_settings_ibfk_1", type_="foreignkey")
|
||||
|
||||
# Alter columns now that foreign keys are dropped
|
||||
op.alter_column("bw_custom_configs", "service_id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=True)
|
||||
|
||||
op.alter_column("bw_jobs_cache", "service_id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=True)
|
||||
|
||||
op.alter_column("bw_services", "id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_services_settings", "service_id", existing_type=mysql.VARCHAR(length=64), type_=sa.String(length=256), existing_nullable=False)
|
||||
|
||||
# After altering, recreate the foreign keys with updated references
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.create_foreign_key("bw_custom_configs_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_2", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.create_foreign_key("bw_services_settings_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
# Update bw_settings.default from String(4096) to TEXT
|
||||
op.alter_column("bw_settings", "default", existing_type=mysql.VARCHAR(length=4096), type_=sa.TEXT(), existing_nullable=True)
|
||||
|
||||
# Drop bw_ui_users.id column
|
||||
op.drop_column("bw_ui_users", "id")
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-rc1' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the changes:
|
||||
# 1. Drop foreign keys
|
||||
# 2. Revert column types
|
||||
# 3. Recreate foreign keys
|
||||
# 4. Revert version
|
||||
# 5. Re-add bw_ui_users.id column
|
||||
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.drop_constraint("bw_custom_configs_ibfk_1", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_ibfk_1", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.drop_constraint("bw_services_settings_ibfk_1", type_="foreignkey")
|
||||
|
||||
op.alter_column("bw_services_settings", "service_id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_services", "id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_jobs_cache", "service_id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=True)
|
||||
|
||||
op.alter_column("bw_custom_configs", "service_id", existing_type=sa.String(length=256), type_=mysql.VARCHAR(length=64), existing_nullable=True)
|
||||
|
||||
# Recreate foreign keys with old definitions
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.create_foreign_key("bw_custom_configs_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.create_foreign_key("bw_services_settings_ibfk_1", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
op.alter_column("bw_settings", "default", existing_type=sa.TEXT(), type_=mysql.VARCHAR(length=4096), existing_nullable=True)
|
||||
|
||||
# Re-add bw_ui_users.id and index on username
|
||||
op.add_column("bw_ui_users", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False))
|
||||
|
||||
# Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
"""Upgrade to version 1.6.0-beta
|
||||
|
||||
Revision ID: 839424d81cf7
|
||||
Revises: 3d6af0bf1bec
|
||||
Create Date: 2024-12-19 13:34:03.714317
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "839424d81cf7"
|
||||
down_revision: Union[str, None] = "3d6af0bf1bec"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Define old and new ENUMs for methods_enum (adding "wizard")
|
||||
old_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
new_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum")
|
||||
|
||||
# Alter columns that use methods_enum
|
||||
op.alter_column("bw_plugins", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False, existing_server_default="manual")
|
||||
|
||||
op.alter_column("bw_services", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_custom_configs", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False)
|
||||
|
||||
op.alter_column("bw_ui_users", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False, existing_server_default="manual")
|
||||
|
||||
# Update custom_configs_types_enum (adding "default_server_stream", "crs_plugins_before", "crs_plugins_after")
|
||||
old_cct_enum = sa.Enum("http", "stream", "server_http", "server_stream", "default_server_http", "modsec", "modsec_crs", name="custom_configs_types_enum")
|
||||
new_cct_enum = sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
)
|
||||
|
||||
op.alter_column("bw_custom_configs", "type", existing_type=old_cct_enum, type_=new_cct_enum, existing_nullable=False)
|
||||
|
||||
# Update plugin_types_enum (adding "ui")
|
||||
old_pt_enum = sa.Enum("core", "external", "pro", name="plugin_types_enum")
|
||||
new_pt_enum = sa.Enum("core", "external", "ui", "pro", name="plugin_types_enum")
|
||||
|
||||
op.alter_column("bw_plugins", "type", existing_type=old_pt_enum, type_=new_pt_enum, existing_nullable=False, existing_server_default="core")
|
||||
|
||||
# Create new UI tables
|
||||
op.create_table(
|
||||
"bw_ui_permissions",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
sa.Column("description", sa.String(256), nullable=False),
|
||||
sa.Column("update_datetime", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_recovery_codes",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("code", sa.UnicodeText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_permissions",
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.Column("permission_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["permission_name"], ["bw_ui_permissions.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("role_name", "permission_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_users",
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("user_name", "role_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_sessions",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("ip", sa.String(39), nullable=False),
|
||||
sa.Column("user_agent", sa.TEXT, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_activity", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
from model import JSONText
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_columns_preferences",
|
||||
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("table_name", sa.Enum("bans", "configs", "instances", "jobs", "plugins", "reports", "services", name="tables_enum"), nullable=False),
|
||||
sa.Column("columns", JSONText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("user_name", "table_name", name="uq_user_columns_preferences"),
|
||||
)
|
||||
|
||||
# Templates tables
|
||||
op.create_table(
|
||||
"bw_templates",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("name", sa.String(256), unique=True, nullable=False),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_steps",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False, primary_key=True),
|
||||
sa.Column("title", sa.TEXT, nullable=False),
|
||||
sa.Column("subtitle", sa.TEXT, nullable=True),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id", "template_id"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_settings",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("default", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer(), nullable=True, default=0),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["setting_id"], ["bw_settings.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "setting_id", "step_id", "suffix"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_custom_configs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("type", new_cct_enum, nullable=False),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "step_id", "type", "name"),
|
||||
)
|
||||
|
||||
# bw_jobs changes (add run_async, remove success and last_run)
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.add_column(sa.Column("run_async", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.drop_column("success")
|
||||
batch_op.drop_column("last_run")
|
||||
|
||||
op.create_table(
|
||||
"bw_jobs_runs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("success", sa.Boolean(), nullable=True, server_default="0"),
|
||||
sa.Column("start_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("end_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["job_name"], ["bw_jobs.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
# bw_services add creation_date, last_update
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_update", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
# Set current timestamp for existing services data
|
||||
op.execute("UPDATE bw_services SET creation_date = CURRENT_TIMESTAMP, last_update = CURRENT_TIMESTAMP")
|
||||
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.alter_column("creation_date", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
batch_op.alter_column("last_update", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
|
||||
# bw_ui_users changes
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.add_column(sa.Column("email", sa.String(256), nullable=True, unique=True))
|
||||
batch_op.add_column(sa.Column("admin", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.add_column(sa.Column("theme", sa.Enum("light", "dark", name="themes_enum"), nullable=False, server_default="light"))
|
||||
batch_op.add_column(sa.Column("totp_secret", sa.String(256), nullable=True))
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp()))
|
||||
batch_op.add_column(sa.Column("update_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp()))
|
||||
batch_op.drop_column("is_two_factor_enabled")
|
||||
batch_op.drop_column("secret_token")
|
||||
|
||||
# Set defaults for existing bw_ui_users rows
|
||||
op.execute("UPDATE bw_ui_users SET admin=1, theme='light', creation_date=CURRENT_TIMESTAMP, update_date=CURRENT_TIMESTAMP")
|
||||
|
||||
# bw_plugin_pages changes
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.add_column(sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("checksum", sa.String(128), nullable=False))
|
||||
|
||||
# Add old data to new columns
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugin_pages
|
||||
SET data = template_file, checksum = template_checksum
|
||||
WHERE template_file IS NOT NULL
|
||||
"""
|
||||
)
|
||||
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.drop_column("template_file")
|
||||
batch_op.drop_column("template_checksum")
|
||||
batch_op.drop_column("actions_file")
|
||||
batch_op.drop_column("actions_checksum")
|
||||
batch_op.drop_column("obfuscation_file")
|
||||
batch_op.drop_column("obfuscation_checksum")
|
||||
|
||||
# bw_instances changes
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.add_column(sa.Column("name", sa.String(256), nullable=True))
|
||||
# Add the 'type' column with the old enum definition (or no enum yet if it didn’t exist before)
|
||||
# For instance, if previously there was no enum, start with the initial values:
|
||||
old_it_enum = sa.Enum("static", name="instance_type_enum") # The original value set
|
||||
batch_op.add_column(sa.Column("type", old_it_enum, nullable=True))
|
||||
# Similarly add the 'status', 'method', 'creation_date', 'last_seen' columns
|
||||
old_is_enum = sa.Enum("loading", "up", name="instance_status_enum")
|
||||
batch_op.add_column(sa.Column("status", old_is_enum, nullable=True))
|
||||
|
||||
old_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
batch_op.add_column(sa.Column("method", old_methods_enum, nullable=True))
|
||||
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_seen", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
op.execute("DELETE FROM bw_instances WHERE name IS NULL")
|
||||
|
||||
# Now that columns exist and have values, alter their definitions if needed:
|
||||
new_it_enum = sa.Enum("static", "container", "pod", name="instance_type_enum")
|
||||
op.alter_column("bw_instances", "type", existing_type=old_it_enum, type_=new_it_enum, existing_nullable=False, existing_server_default="static")
|
||||
|
||||
new_is_enum = sa.Enum("loading", "up", "down", name="instance_status_enum")
|
||||
op.alter_column("bw_instances", "status", existing_type=old_is_enum, type_=new_is_enum, existing_nullable=False, existing_server_default="loading")
|
||||
|
||||
# Similarly alter the method column to the new enum with 'wizard' if needed:
|
||||
extended_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum")
|
||||
op.alter_column(
|
||||
"bw_instances", "method", existing_type=old_methods_enum, type_=extended_methods_enum, existing_nullable=False, existing_server_default="manual"
|
||||
)
|
||||
|
||||
# Finally, now set them to NOT NULL
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.alter_column("name", existing_type=sa.String(256), nullable=False)
|
||||
batch_op.alter_column("type", existing_type=new_it_enum, nullable=False)
|
||||
batch_op.alter_column("status", existing_type=new_is_enum, nullable=False)
|
||||
batch_op.alter_column("method", existing_type=extended_methods_enum, nullable=False)
|
||||
batch_op.alter_column("creation_date", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
batch_op.alter_column("last_seen", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
|
||||
# Update version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert enums to their old definitions by altering columns back
|
||||
old_methods_enum = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
# methods_enum had "wizard" added, now remove it
|
||||
op.alter_column(
|
||||
"bw_plugins",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="manual",
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_services",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_custom_configs",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_ui_users",
|
||||
"method",
|
||||
existing_type=sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"),
|
||||
type_=old_methods_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="manual",
|
||||
)
|
||||
|
||||
# custom_configs_types_enum remove "default_server_stream", "crs_plugins_before", "crs_plugins_after"
|
||||
old_cct_enum = sa.Enum("http", "default_server_http", "server_http", "modsec", "modsec_crs", "stream", "server_stream", name="custom_configs_types_enum")
|
||||
op.alter_column(
|
||||
"bw_custom_configs",
|
||||
"type",
|
||||
existing_type=sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
type_=old_cct_enum,
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
# plugin_types_enum remove "ui"
|
||||
old_pt_enum = sa.Enum("core", "external", "pro", name="plugin_types_enum")
|
||||
op.alter_column(
|
||||
"bw_plugins",
|
||||
"type",
|
||||
existing_type=sa.Enum("core", "external", "ui", "pro", name="plugin_types_enum"),
|
||||
type_=old_pt_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="core",
|
||||
)
|
||||
|
||||
# instance_type_enum revert to just "static"
|
||||
old_it_enum = sa.Enum("static", name="instance_type_enum")
|
||||
op.alter_column(
|
||||
"bw_instances",
|
||||
"type",
|
||||
existing_type=sa.Enum("static", "container", "pod", name="instance_type_enum"),
|
||||
type_=old_it_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="static",
|
||||
)
|
||||
|
||||
# instance_status_enum revert to "loading", "up"
|
||||
old_is_enum = sa.Enum("loading", "up", name="instance_status_enum")
|
||||
op.alter_column(
|
||||
"bw_instances",
|
||||
"status",
|
||||
existing_type=sa.Enum("loading", "up", "down", name="instance_status_enum"),
|
||||
type_=old_is_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="loading",
|
||||
)
|
||||
|
||||
# Drop newly created UI and templates tables
|
||||
op.drop_table("bw_template_custom_configs")
|
||||
op.drop_table("bw_template_settings")
|
||||
op.drop_table("bw_template_steps")
|
||||
op.drop_table("bw_templates")
|
||||
op.drop_table("bw_ui_user_columns_preferences")
|
||||
op.drop_table("bw_ui_user_sessions")
|
||||
op.drop_table("bw_ui_roles_users")
|
||||
op.drop_table("bw_ui_roles_permissions")
|
||||
op.drop_table("bw_ui_user_recovery_codes")
|
||||
op.drop_table("bw_ui_roles")
|
||||
op.drop_table("bw_ui_permissions")
|
||||
|
||||
# Revert bw_settings constraints: old primary key was (id,name) and there was a unique constraint on id
|
||||
op.drop_constraint("bw_settings_name_key", "bw_settings", type_="unique")
|
||||
op.drop_constraint("bw_settings_pkey", "bw_settings", type_="primary")
|
||||
|
||||
# Recreate old constraints:
|
||||
# PrimaryKeyConstraint("id", "name"), UniqueConstraint("id")
|
||||
# Note: The original primary key and unique constraints must be re-added as they were initially.
|
||||
op.create_primary_key("bw_settings_pkey", "bw_settings", ["id", "name"])
|
||||
op.create_unique_constraint("id", "bw_settings", ["id"])
|
||||
|
||||
# bw_jobs revert: drop run_async, add success and last_run
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.drop_column("run_async")
|
||||
batch_op.add_column(sa.Column("success", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_run", sa.DateTime(), nullable=True))
|
||||
|
||||
# Drop bw_jobs_runs table
|
||||
op.drop_table("bw_jobs_runs")
|
||||
|
||||
# bw_services revert: remove creation_date, last_update
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.drop_column("last_update")
|
||||
batch_op.drop_column("creation_date")
|
||||
|
||||
# bw_ui_users revert: drop email, admin, theme, totp_secret, creation_date, update_date
|
||||
# add back is_two_factor_enabled, secret_token
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.drop_column("email")
|
||||
batch_op.drop_column("admin")
|
||||
batch_op.drop_column("theme")
|
||||
batch_op.drop_column("totp_secret")
|
||||
batch_op.drop_column("creation_date")
|
||||
batch_op.drop_column("update_date")
|
||||
batch_op.add_column(sa.Column("is_two_factor_enabled", sa.Boolean, nullable=False, server_default="0"))
|
||||
batch_op.add_column(sa.Column("secret_token", sa.String(32), nullable=True, unique=True, default=None))
|
||||
|
||||
# bw_plugin_pages revert: remove data, checksum, add template_file, template_checksum, actions_file, actions_checksum, obfuscation_file, obfuscation_checksum
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.drop_column("data")
|
||||
batch_op.drop_column("checksum")
|
||||
batch_op.add_column(sa.Column("template_file", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("template_checksum", sa.String(128), nullable=False))
|
||||
batch_op.add_column(sa.Column("actions_file", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("actions_checksum", sa.String(128), nullable=False))
|
||||
batch_op.add_column(sa.Column("obfuscation_file", sa.LargeBinary(length=(2**32) - 1), nullable=True))
|
||||
batch_op.add_column(sa.Column("obfuscation_checksum", sa.String(128), nullable=True))
|
||||
|
||||
# bw_instances revert: drop name, type, status, method, creation_date, last_seen
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.drop_column("last_seen")
|
||||
batch_op.drop_column("creation_date")
|
||||
batch_op.drop_column("method")
|
||||
batch_op.drop_column("status")
|
||||
batch_op.drop_column("type")
|
||||
batch_op.drop_column("name")
|
||||
|
||||
# Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.10
|
||||
|
||||
Revision ID: b03a64d4d34a
|
||||
Revises: c6fcd4f6971d
|
||||
Create Date: 2024-12-19 13:28:59.190577
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "b03a64d4d34a"
|
||||
down_revision: Union[str, None] = "c6fcd4f6971d"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata' back to 1.5.9
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
"""Upgrade to version 1.5.8
|
||||
|
||||
Revision ID: b45f98a50b5c
|
||||
Revises: dd15cf86a200
|
||||
Create Date: 2024-12-19 13:26:03.280603
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "b45f98a50b5c"
|
||||
down_revision: Union[str, None] = "dd15cf86a200"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add new columns to 'bw_metadata'
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("pro_license", sa.String(length=128), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_custom_configs_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_external_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_pro_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_instances_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("failover", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Add new columns to 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_config_change", sa.DateTime(), nullable=True))
|
||||
|
||||
# Set default value for 'config_changed' in 'bw_plugins'
|
||||
op.execute("UPDATE bw_plugins SET config_changed = false")
|
||||
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert 'bw_plugins' changes
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("last_config_change")
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Revert 'bw_metadata' changes
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("failover")
|
||||
batch_op.drop_column("last_instances_change")
|
||||
batch_op.drop_column("last_pro_plugins_change")
|
||||
batch_op.drop_column("last_external_plugins_change")
|
||||
batch_op.drop_column("last_custom_configs_change")
|
||||
batch_op.drop_column("pro_license")
|
||||
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7', config_changed = false WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.9
|
||||
|
||||
Revision ID: c6fcd4f6971d
|
||||
Revises: b45f98a50b5c
|
||||
Create Date: 2024-12-19 13:27:29.252130
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "c6fcd4f6971d"
|
||||
down_revision: Union[str, None] = "b45f98a50b5c"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
"""Upgrade to version 1.5.7
|
||||
|
||||
Revision ID: dd15cf86a200
|
||||
Revises: 021e3123e517
|
||||
Create Date: 2024-12-19 13:24:36.941477
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "dd15cf86a200"
|
||||
down_revision: Union[str, None] = "021e3123e517"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create new table for bw_cli_commands
|
||||
op.create_table(
|
||||
"bw_cli_commands",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(always=False, start=1, increment=1), nullable=False),
|
||||
sa.Column("name", sa.String(length=64), nullable=False),
|
||||
sa.Column("plugin_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("file_name", sa.String(length=256), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="cascade", ondelete="cascade"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("plugin_id", "name"),
|
||||
)
|
||||
|
||||
# Handle foreign key constraints for bw_jobs_cache
|
||||
op.drop_constraint("fk_bw_jobs_cache_job_name", "bw_jobs_cache", type_="foreignkey")
|
||||
op.create_foreign_key(None, "bw_jobs_cache", "bw_jobs", ["job_name"], ["name"], onupdate="cascade", ondelete="cascade")
|
||||
|
||||
# Add new columns to bw_plugin_pages
|
||||
op.add_column(
|
||||
"bw_plugin_pages",
|
||||
sa.Column("obfuscation_file", mysql.LONGBLOB(), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"bw_plugin_pages",
|
||||
sa.Column("obfuscation_checksum", sa.String(length=128), nullable=True),
|
||||
)
|
||||
|
||||
# Add the new order column to bw_settings
|
||||
op.add_column("bw_settings", sa.Column("order", sa.Integer(), nullable=False))
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the version update
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.6' WHERE id = 1")
|
||||
|
||||
# Reverse the addition of the order column
|
||||
op.drop_column("bw_settings", "order")
|
||||
|
||||
# Reverse changes in bw_plugin_pages
|
||||
op.drop_column("bw_plugin_pages", "obfuscation_checksum")
|
||||
op.drop_column("bw_plugin_pages", "obfuscation_file")
|
||||
|
||||
# Restore foreign key constraints for bw_jobs_cache
|
||||
op.drop_constraint(None, "bw_jobs_cache", type_="foreignkey")
|
||||
op.create_foreign_key("fk_bw_jobs_cache_job_name", "bw_jobs_cache", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
# Drop the newly created table bw_cli_commands
|
||||
op.drop_table("bw_cli_commands")
|
||||
|
||||
# Revert the version update
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.6' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.2
|
||||
|
||||
Revision ID: e4ccc523fea5
|
||||
Revises: edcde398c829
|
||||
Create Date: 2024-12-19 13:17:25.789125
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "e4ccc523fea5"
|
||||
down_revision: Union[str, None] = "edcde398c829"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.1
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.1' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.3
|
||||
|
||||
Revision ID: e65b18370d91
|
||||
Revises: e4ccc523fea5
|
||||
Create Date: 2024-12-19 13:18:36.153688
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "e65b18370d91"
|
||||
down_revision: Union[str, None] = "e4ccc523fea5"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.3
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Upgrade to version 1.5.1
|
||||
|
||||
Revision ID: edcde398c829
|
||||
Revises: 41d395e48cfd
|
||||
Create Date: 2024-12-19 13:16:34.086006
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "edcde398c829"
|
||||
down_revision: Union[str, None] = "41d395e48cfd"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Add new columns to bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("scheduler_first_start", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("custom_configs_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("external_plugins_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("instances_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET scheduler_first_start = false,
|
||||
custom_configs_changed = false,
|
||||
external_plugins_changed = false,
|
||||
config_changed = false,
|
||||
instances_changed = false,
|
||||
version = '1.5.1'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Remove new columns from bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.drop_column("instances_changed")
|
||||
batch_op.drop_column("config_changed")
|
||||
batch_op.drop_column("external_plugins_changed")
|
||||
batch_op.drop_column("custom_configs_changed")
|
||||
batch_op.drop_column("scheduler_first_start")
|
||||
|
||||
# Revert the version back to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.10
|
||||
|
||||
Revision ID: 0a2e336b02e7
|
||||
Revises: fe047f892d6b
|
||||
Create Date: 2024-12-19 14:42:04.961295
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0a2e336b02e7"
|
||||
down_revision: Union[str, None] = "fe047f892d6b"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata' back to 1.5.9
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
"""Upgrade to version 1.6.0-beta
|
||||
|
||||
Revision ID: 0b08c406d820
|
||||
Revises: fbd680c6ffeb
|
||||
Create Date: 2024-12-19 14:47:13.427196
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0b08c406d820"
|
||||
down_revision: Union[str, None] = "fbd680c6ffeb"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Use create_type=False since these enums already exist
|
||||
old_methods_enum = postgresql.ENUM("ui", "scheduler", "autoconf", "manual", name="methods_enum", create_type=False)
|
||||
new_methods_enum = postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False)
|
||||
|
||||
old_cct_enum = postgresql.ENUM(
|
||||
"http", "stream", "server_http", "server_stream", "default_server_http", "modsec", "modsec_crs", name="custom_configs_types_enum", create_type=False
|
||||
)
|
||||
new_cct_enum = postgresql.ENUM(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
create_type=False,
|
||||
)
|
||||
|
||||
old_pt_enum = postgresql.ENUM("core", "external", "pro", name="plugin_types_enum", create_type=False)
|
||||
new_pt_enum = postgresql.ENUM("core", "external", "ui", "pro", name="plugin_types_enum", create_type=False)
|
||||
|
||||
# Add new enum values if not already present
|
||||
op.execute("ALTER TYPE custom_configs_types_enum ADD VALUE IF NOT EXISTS 'default_server_stream'")
|
||||
op.execute("ALTER TYPE custom_configs_types_enum ADD VALUE IF NOT EXISTS 'crs_plugins_before'")
|
||||
op.execute("ALTER TYPE custom_configs_types_enum ADD VALUE IF NOT EXISTS 'crs_plugins_after'")
|
||||
|
||||
op.execute("ALTER TYPE plugin_types_enum ADD VALUE IF NOT EXISTS 'ui'")
|
||||
|
||||
# Alter columns that rely on these enums
|
||||
op.alter_column("bw_plugins", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False, existing_server_default="manual")
|
||||
op.alter_column("bw_services", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False)
|
||||
op.alter_column("bw_custom_configs", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False)
|
||||
op.alter_column("bw_ui_users", "method", existing_type=old_methods_enum, type_=new_methods_enum, existing_nullable=False, existing_server_default="manual")
|
||||
|
||||
op.alter_column("bw_custom_configs", "type", existing_type=old_cct_enum, type_=new_cct_enum, existing_nullable=False)
|
||||
op.alter_column("bw_plugins", "type", existing_type=old_pt_enum, type_=new_pt_enum, existing_nullable=False, existing_server_default="core")
|
||||
|
||||
# Create new UI tables
|
||||
op.create_table(
|
||||
"bw_ui_permissions",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
sa.Column("description", sa.String(256), nullable=False),
|
||||
sa.Column("update_datetime", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_recovery_codes",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("code", sa.UnicodeText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_permissions",
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.Column("permission_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["permission_name"], ["bw_ui_permissions.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("role_name", "permission_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_users",
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("user_name", "role_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_sessions",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("ip", sa.String(39), nullable=False),
|
||||
sa.Column("user_agent", sa.TEXT, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_activity", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
from model import JSONText
|
||||
|
||||
tables_enum = postgresql.ENUM("bans", "configs", "instances", "jobs", "plugins", "reports", "services", name="tables_enum", create_type=False)
|
||||
|
||||
op.execute("CREATE TYPE tables_enum AS ENUM ('bans', 'configs', 'instances', 'jobs', 'plugins', 'reports', 'services')")
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_columns_preferences",
|
||||
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("table_name", tables_enum, nullable=False),
|
||||
sa.Column("columns", JSONText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("user_name", "table_name", name="uq_user_columns_preferences"),
|
||||
)
|
||||
|
||||
# Templates tables
|
||||
op.create_table(
|
||||
"bw_templates",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("name", sa.String(256), unique=True, nullable=False),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_steps",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False, primary_key=True),
|
||||
sa.Column("title", sa.Text, nullable=False),
|
||||
sa.Column("subtitle", sa.Text, nullable=True),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id", "template_id"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_settings",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("default", sa.Text, nullable=False),
|
||||
sa.Column("suffix", sa.Integer(), nullable=True, default=0),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["setting_id"], ["bw_settings.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "setting_id", "step_id", "suffix"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_custom_configs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("type", new_cct_enum, nullable=False),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "step_id", "type", "name"),
|
||||
)
|
||||
|
||||
# bw_jobs changes (add run_async, remove success and last_run)
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.add_column(sa.Column("run_async", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.drop_column("success")
|
||||
batch_op.drop_column("last_run")
|
||||
|
||||
op.create_table(
|
||||
"bw_jobs_runs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("success", sa.Boolean(), nullable=True, server_default="0"),
|
||||
sa.Column("start_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.Column("end_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["job_name"], ["bw_jobs.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
# bw_services add creation_date, last_update
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True, server_default=sa.text("CURRENT_TIMESTAMP")))
|
||||
batch_op.add_column(sa.Column("last_update", sa.DateTime(timezone=True), nullable=True, server_default=sa.text("CURRENT_TIMESTAMP")))
|
||||
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.alter_column("creation_date", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
batch_op.alter_column("last_update", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
|
||||
# bw_ui_users changes
|
||||
themes_enum = postgresql.ENUM("light", "dark", name="themes_enum", create_type=False)
|
||||
|
||||
op.execute("CREATE TYPE themes_enum AS ENUM ('light', 'dark')")
|
||||
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.add_column(sa.Column("email", sa.String(256), nullable=True, unique=True))
|
||||
batch_op.add_column(sa.Column("admin", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.add_column(sa.Column("theme", themes_enum, nullable=False, server_default="light"))
|
||||
batch_op.add_column(sa.Column("totp_secret", sa.String(256), nullable=True))
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")))
|
||||
batch_op.add_column(sa.Column("update_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")))
|
||||
batch_op.drop_column("is_two_factor_enabled")
|
||||
batch_op.drop_column("secret_token")
|
||||
|
||||
op.execute("UPDATE bw_ui_users SET admin=TRUE, theme='light', creation_date=CURRENT_TIMESTAMP, update_date=CURRENT_TIMESTAMP")
|
||||
|
||||
# bw_plugin_pages changes
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.add_column(sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False))
|
||||
batch_op.add_column(sa.Column("checksum", sa.String(128), nullable=False))
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugin_pages
|
||||
SET data = template_file, checksum = template_checksum
|
||||
WHERE template_file IS NOT NULL
|
||||
"""
|
||||
)
|
||||
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.drop_column("template_file")
|
||||
batch_op.drop_column("template_checksum")
|
||||
batch_op.drop_column("actions_file")
|
||||
batch_op.drop_column("actions_checksum")
|
||||
batch_op.drop_column("obfuscation_file")
|
||||
batch_op.drop_column("obfuscation_checksum")
|
||||
|
||||
# bw_instances changes
|
||||
old_methods_enum_for_instances = postgresql.ENUM("ui", "scheduler", "autoconf", "manual", name="methods_enum", create_type=False)
|
||||
|
||||
# Add new enum values for instances if needed before altering columns
|
||||
op.execute("ALTER TYPE methods_enum ADD VALUE IF NOT EXISTS 'wizard'")
|
||||
|
||||
new_it_enum = postgresql.ENUM("static", "container", "pod", name="instance_type_enum", create_type=False)
|
||||
new_is_enum = postgresql.ENUM("loading", "up", "down", name="instance_status_enum", create_type=False)
|
||||
|
||||
op.execute("CREATE TYPE instance_type_enum AS ENUM ('static', 'container', 'pod')")
|
||||
op.execute("CREATE TYPE instance_status_enum AS ENUM ('loading', 'up', 'down')")
|
||||
|
||||
extended_methods_enum = postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False)
|
||||
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.add_column(sa.Column("name", sa.String(256), nullable=True))
|
||||
batch_op.add_column(sa.Column("type", new_it_enum, nullable=True))
|
||||
batch_op.add_column(sa.Column("status", new_it_enum, nullable=True))
|
||||
batch_op.add_column(sa.Column("method", old_methods_enum_for_instances, nullable=True))
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True, server_default=sa.text("CURRENT_TIMESTAMP")))
|
||||
batch_op.add_column(sa.Column("last_seen", sa.DateTime(timezone=True), nullable=True, server_default=sa.text("CURRENT_TIMESTAMP")))
|
||||
|
||||
op.execute("DELETE FROM bw_instances WHERE name IS NULL")
|
||||
|
||||
op.alter_column(
|
||||
"bw_instances",
|
||||
"method",
|
||||
existing_type=old_methods_enum_for_instances,
|
||||
type_=extended_methods_enum,
|
||||
existing_nullable=False,
|
||||
existing_server_default="manual",
|
||||
)
|
||||
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.alter_column("name", existing_type=sa.String(256), nullable=False)
|
||||
batch_op.alter_column("type", existing_type=new_it_enum, nullable=False)
|
||||
batch_op.alter_column("status", existing_type=new_is_enum, nullable=False)
|
||||
batch_op.alter_column("method", existing_type=extended_methods_enum, nullable=False)
|
||||
batch_op.alter_column("creation_date", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
batch_op.alter_column("last_seen", existing_type=sa.DateTime(timezone=True), nullable=False)
|
||||
|
||||
# Update version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Reverse changes:
|
||||
# 1. Drop added columns from bw_instances
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.drop_column("last_seen")
|
||||
batch_op.drop_column("creation_date")
|
||||
batch_op.drop_column("method")
|
||||
batch_op.drop_column("status")
|
||||
batch_op.drop_column("type")
|
||||
batch_op.drop_column("name")
|
||||
|
||||
# 2. Revert bw_plugin_pages to old structure
|
||||
op.create_table(
|
||||
"bw_plugin_pages_old",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False),
|
||||
sa.Column("template_file", sa.LargeBinary(length=(2**32)), nullable=False),
|
||||
sa.Column("template_checksum", sa.String(128), nullable=False),
|
||||
sa.Column("actions_file", sa.LargeBinary(length=(2**32)), nullable=False),
|
||||
sa.Column("actions_checksum", sa.String(128), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_plugin_pages_old (id, plugin_id, template_file, template_checksum, actions_file, actions_checksum)
|
||||
SELECT id, plugin_id, NULL, '', NULL, ''
|
||||
FROM bw_plugin_pages
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_plugin_pages")
|
||||
op.rename_table("bw_plugin_pages_old", "bw_plugin_pages")
|
||||
|
||||
# 3. Revert bw_ui_users to old structure
|
||||
op.create_table(
|
||||
"bw_ui_users_old",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("username", sa.String(256), nullable=False, unique=True),
|
||||
sa.Column("password", sa.String(60), nullable=False),
|
||||
sa.Column("is_two_factor_enabled", sa.Boolean(), nullable=False, server_default="0"),
|
||||
sa.Column("secret_token", sa.String(32), nullable=True, unique=True, default=None),
|
||||
sa.Column("method", sa.String(16), nullable=False, server_default="manual"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_ui_users_old (id, username, password, is_two_factor_enabled, secret_token, method)
|
||||
SELECT 1, username, password, '0', NULL, 'manual'
|
||||
FROM bw_ui_users
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_ui_users")
|
||||
op.rename_table("bw_ui_users_old", "bw_ui_users")
|
||||
|
||||
# 4. Drop new UI and templates tables
|
||||
op.drop_table("bw_template_custom_configs")
|
||||
op.drop_table("bw_template_settings")
|
||||
op.drop_table("bw_template_steps")
|
||||
op.drop_table("bw_templates")
|
||||
op.drop_table("bw_ui_user_columns_preferences")
|
||||
op.drop_table("bw_ui_user_sessions")
|
||||
op.drop_table("bw_ui_roles_users")
|
||||
op.drop_table("bw_ui_roles_permissions")
|
||||
op.drop_table("bw_ui_user_recovery_codes")
|
||||
op.drop_table("bw_ui_roles")
|
||||
op.drop_table("bw_ui_permissions")
|
||||
|
||||
# 5. Drop new enums
|
||||
op.execute("DROP TYPE IF EXISTS themes_enum")
|
||||
op.execute("DROP TYPE IF EXISTS tables_enum")
|
||||
op.execute("DROP TYPE IF EXISTS instance_status_enum")
|
||||
|
||||
# 6. Drop new enum values
|
||||
op.execute("ALTER TYPE methods_enum DROP VALUE IF EXISTS 'wizard'")
|
||||
op.execute("ALTER TYPE custom_configs_types_enum DROP VALUE IF EXISTS 'default_server_stream'")
|
||||
op.execute("ALTER TYPE custom_configs_types_enum DROP VALUE IF EXISTS 'crs_plugins_before'")
|
||||
op.execute("ALTER TYPE custom_configs_types_enum DROP VALUE IF EXISTS 'crs_plugins_after'")
|
||||
op.execute("ALTER TYPE plugin_types_enum DROP VALUE IF EXISTS 'ui'")
|
||||
|
||||
# 7. Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Upgrade to version 1.5.1
|
||||
|
||||
Revision ID: 3133d7320b63
|
||||
Revises: 4badb3a66963
|
||||
Create Date: 2024-12-19 13:39:17.647342
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "3133d7320b63"
|
||||
down_revision: Union[str, None] = "4badb3a66963"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Add new columns to bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("scheduler_first_start", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("custom_configs_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("external_plugins_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("instances_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET scheduler_first_start = false,
|
||||
custom_configs_changed = false,
|
||||
external_plugins_changed = false,
|
||||
config_changed = false,
|
||||
instances_changed = false,
|
||||
version = '1.5.1'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Remove new columns from bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.drop_column("instances_changed")
|
||||
batch_op.drop_column("config_changed")
|
||||
batch_op.drop_column("external_plugins_changed")
|
||||
batch_op.drop_column("custom_configs_changed")
|
||||
batch_op.drop_column("scheduler_first_start")
|
||||
|
||||
# Revert the version back to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.2
|
||||
|
||||
Revision ID: 4a2457daed53
|
||||
Revises: 3133d7320b63
|
||||
Create Date: 2024-12-19 13:40:12.477741
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "4a2457daed53"
|
||||
down_revision: Union[str, None] = "3133d7320b63"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.1
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.1' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
"""Upgrade to version 1.5.0
|
||||
|
||||
Revision ID: 4badb3a66963
|
||||
Revises:
|
||||
Create Date: 2024-12-19 13:36:21.097290
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "4badb3a66963"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.drop_column("bw_plugins", "order")
|
||||
|
||||
# Data migration: Update the version to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Step 1: Add 'order' column back to 'bw_plugins' (with a default value of 0 for NOT NULL constraint)
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("order", sa.Integer(), autoincrement=False, nullable=True, server_default="0"))
|
||||
|
||||
# Step 2: Set default value for existing rows
|
||||
op.execute("UPDATE bw_plugins SET `order` = 0")
|
||||
|
||||
# Step 3: Alter 'order' column to NOT NULL
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.alter_column("order", nullable=False)
|
||||
|
||||
# Data migration: Update the version to 1.5.0-beta
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
"""Upgrade to version 1.5.7
|
||||
|
||||
Revision ID: 5201c88f004d
|
||||
Revises: b4abd1acf9f1
|
||||
Create Date: 2024-12-19 14:36:04.319448
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "5201c88f004d"
|
||||
down_revision: Union[str, None] = "b4abd1acf9f1"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
|
||||
# Create new table for bw_cli_commands
|
||||
op.create_table(
|
||||
"bw_cli_commands",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(always=False, start=1, increment=1), nullable=False),
|
||||
sa.Column("name", sa.String(length=64), nullable=False),
|
||||
sa.Column("plugin_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("file_name", sa.String(length=256), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="cascade", ondelete="cascade"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("plugin_id", "name"),
|
||||
)
|
||||
|
||||
# Handle foreign key constraints for bw_jobs_cache
|
||||
fk_result = conn.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'bw_jobs_cache'::regclass
|
||||
AND confrelid = 'bw_jobs'::regclass
|
||||
AND conname = 'fk_bw_jobs_cache_job_name'
|
||||
"""
|
||||
)
|
||||
).fetchone()
|
||||
|
||||
if fk_result:
|
||||
op.drop_constraint("fk_bw_jobs_cache_job_name", "bw_jobs_cache", type_="foreignkey")
|
||||
|
||||
op.create_foreign_key(None, "bw_jobs_cache", "bw_jobs", ["job_name"], ["name"], onupdate="cascade", ondelete="cascade")
|
||||
|
||||
# Add new columns to bw_plugin_pages
|
||||
op.add_column("bw_plugin_pages", sa.Column("obfuscation_file", sa.LargeBinary(length=4294967295), nullable=True))
|
||||
op.add_column("bw_plugin_pages", sa.Column("obfuscation_checksum", sa.String(length=128), nullable=True))
|
||||
|
||||
# Add the new order column to bw_settings
|
||||
# Step 1: Add column as nullable
|
||||
op.add_column("bw_settings", sa.Column("order", sa.Integer(), nullable=True))
|
||||
|
||||
# Step 2: Populate default values
|
||||
op.execute('UPDATE bw_settings SET "order" = 0')
|
||||
|
||||
# Step 3: Alter column to NOT NULL
|
||||
op.alter_column("bw_settings", "order", nullable=False)
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
|
||||
# Reverse the version update
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.6' WHERE id = 1")
|
||||
|
||||
# Reverse the addition of the order column
|
||||
op.drop_column("bw_settings", "order")
|
||||
|
||||
# Reverse changes in bw_plugin_pages
|
||||
op.drop_column("bw_plugin_pages", "obfuscation_checksum")
|
||||
op.drop_column("bw_plugin_pages", "obfuscation_file")
|
||||
|
||||
# Restore foreign key constraints for bw_jobs_cache
|
||||
fk_result = conn.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'bw_jobs_cache'::regclass
|
||||
AND confrelid = 'bw_jobs'::regclass
|
||||
AND conname IS NULL
|
||||
"""
|
||||
)
|
||||
).fetchone()
|
||||
|
||||
if fk_result:
|
||||
op.drop_constraint(None, "bw_jobs_cache", type_="foreignkey")
|
||||
|
||||
op.create_foreign_key("fk_bw_jobs_cache_job_name", "bw_jobs_cache", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
# Drop the newly created table bw_cli_commands
|
||||
op.drop_table("bw_cli_commands")
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
"""Upgrade to version 1.5.4
|
||||
|
||||
Revision ID: 6b1db23ec3ab
|
||||
Revises: e9b703e3d747
|
||||
Create Date: 2024-12-19 13:46:07.676758
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "6b1db23ec3ab"
|
||||
down_revision: Union[str, None] = "e9b703e3d747"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create new table 'bw_ui_users'
|
||||
op.create_table(
|
||||
"bw_ui_users",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("username", sa.String(length=256), nullable=False),
|
||||
sa.Column("password", sa.String(length=60), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("username"),
|
||||
)
|
||||
|
||||
# Update metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop the table 'bw_ui_users'
|
||||
op.drop_table("bw_ui_users")
|
||||
|
||||
# Revert metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
"""Upgrade to version 1.5.5
|
||||
|
||||
Revision ID: 7deca2941c74
|
||||
Revises: 6b1db23ec3ab
|
||||
Create Date: 2024-12-19 13:47:10.128402
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "7deca2941c74"
|
||||
down_revision: Union[str, None] = "6b1db23ec3ab"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add new columns to bw_ui_users
|
||||
op.add_column("bw_ui_users", sa.Column("is_two_factor_enabled", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_ui_users", sa.Column("secret_token", sa.String(length=32), nullable=True))
|
||||
op.add_column(
|
||||
"bw_ui_users", sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum"), nullable=False, server_default="manual")
|
||||
)
|
||||
op.create_unique_constraint("uq_bw_ui_users_secret_token", "bw_ui_users", ["secret_token"])
|
||||
|
||||
# Increase column sizes
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.VARCHAR(length=4096), type_=sa.String(length=8192), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.VARCHAR(length=4096), type_=sa.String(length=8192), existing_nullable=False)
|
||||
|
||||
# Update all new columns in a single statement
|
||||
op.execute("UPDATE bw_ui_users SET is_two_factor_enabled = false, method = 'manual'")
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert changes to 'bw_ui_users'
|
||||
op.drop_constraint("uq_bw_ui_users_secret_token", "bw_ui_users", type_="unique")
|
||||
op.drop_column("bw_ui_users", "method")
|
||||
op.drop_column("bw_ui_users", "secret_token")
|
||||
op.drop_column("bw_ui_users", "is_two_factor_enabled")
|
||||
|
||||
# Revert column sizes for VARCHAR
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.String(length=8192), type_=sa.VARCHAR(length=4096), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.String(length=8192), type_=sa.VARCHAR(length=4096), existing_nullable=False)
|
||||
|
||||
# Revert version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
"""Upgrade to version 1.6.0-rc1
|
||||
|
||||
Revision ID: 940350925f36
|
||||
Revises: 0b08c406d820
|
||||
Create Date: 2024-12-20 11:02:59.703530
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "940350925f36"
|
||||
down_revision: Union[str, None] = "0b08c406d820"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Drop foreign keys referencing bw_services
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.drop_constraint("bw_custom_configs_service_id_fkey", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.drop_constraint("bw_services_settings_service_id_fkey", type_="foreignkey")
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_service_id_fkey", type_="foreignkey")
|
||||
|
||||
# Create the new bw_services table with updated schema
|
||||
op.create_table(
|
||||
"bw_services_new",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("method", postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False), nullable=False),
|
||||
sa.Column("is_draft", sa.Boolean, default=False, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
# Copy data from old bw_services to bw_services_new
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_new (id, method, is_draft, creation_date, last_update)
|
||||
SELECT id, method, is_draft, creation_date, last_update
|
||||
FROM bw_services
|
||||
"""
|
||||
)
|
||||
|
||||
# Drop old bw_services table now that foreign keys are removed
|
||||
op.drop_table("bw_services")
|
||||
|
||||
# Rename new table to bw_services
|
||||
op.rename_table("bw_services_new", "bw_services")
|
||||
|
||||
# bw_services_settings
|
||||
op.create_table(
|
||||
"bw_services_settings_new",
|
||||
sa.Column("service_id", sa.String(256), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("value", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer, nullable=True, default=0),
|
||||
sa.Column("method", postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False), nullable=False),
|
||||
sa.PrimaryKeyConstraint("service_id", "setting_id", "suffix"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_settings_new (service_id, setting_id, value, suffix, method)
|
||||
SELECT service_id, setting_id, value, suffix, method FROM bw_services_settings
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services_settings")
|
||||
op.rename_table("bw_services_settings_new", "bw_services_settings")
|
||||
|
||||
# bw_custom_configs
|
||||
op.create_table(
|
||||
"bw_custom_configs_new",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("service_id", sa.String(256), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
postgresql.ENUM(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
create_type=False,
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.Column("method", postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False), nullable=False),
|
||||
sa.UniqueConstraint("service_id", "type", "name"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_custom_configs_new (id, service_id, type, name, data, checksum, method)
|
||||
SELECT id, service_id, type, name, data, checksum, method
|
||||
FROM bw_custom_configs
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_custom_configs")
|
||||
op.rename_table("bw_custom_configs_new", "bw_custom_configs")
|
||||
|
||||
# bw_jobs_cache
|
||||
op.create_table(
|
||||
"bw_jobs_cache_new",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("service_id", sa.String(256), nullable=True),
|
||||
sa.Column("file_name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=True),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("checksum", sa.String(128), nullable=True),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_jobs_cache_new (id, job_name, service_id, file_name, data, last_update, checksum)
|
||||
SELECT id, job_name, service_id, file_name, data, last_update, checksum FROM bw_jobs_cache
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_jobs_cache")
|
||||
op.rename_table("bw_jobs_cache_new", "bw_jobs_cache")
|
||||
|
||||
# Recreate foreign keys referencing bw_services now that bw_services is updated
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.create_foreign_key("bw_custom_configs_service_id_fkey", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.create_foreign_key("bw_services_settings_service_id_fkey", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("bw_jobs_cache_service_id_fkey", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
# Update the version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-rc1' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop foreign keys referencing bw_services before reverting changes
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("bw_jobs_cache_service_id_fkey", type_="foreignkey")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.drop_constraint("bw_services_settings_service_id_fkey", type_="foreignkey")
|
||||
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.drop_constraint("bw_custom_configs_service_id_fkey", type_="foreignkey")
|
||||
|
||||
# Revert bw_jobs_cache
|
||||
op.create_table(
|
||||
"bw_jobs_cache_old",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("service_id", sa.String(64), nullable=True),
|
||||
sa.Column("file_name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=True),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("checksum", sa.String(128), nullable=True),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_jobs_cache_old (id, job_name, service_id, file_name, data, last_update, checksum)
|
||||
SELECT id, job_name, service_id, file_name, data, last_update, checksum FROM bw_jobs_cache
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_jobs_cache")
|
||||
op.rename_table("bw_jobs_cache_old", "bw_jobs_cache")
|
||||
|
||||
# Revert bw_custom_configs
|
||||
op.create_table(
|
||||
"bw_custom_configs_old",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("service_id", sa.String(64), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
postgresql.ENUM(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
create_type=False,
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.Column(
|
||||
"method",
|
||||
postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False),
|
||||
nullable=False,
|
||||
),
|
||||
sa.UniqueConstraint("service_id", "type", "name"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_custom_configs_old (id, service_id, type, name, data, checksum, method)
|
||||
SELECT id, service_id, type, name, data, checksum, method FROM bw_custom_configs
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_custom_configs")
|
||||
op.rename_table("bw_custom_configs_old", "bw_custom_configs")
|
||||
|
||||
# Revert bw_services_settings
|
||||
op.create_table(
|
||||
"bw_services_settings_old",
|
||||
sa.Column("service_id", sa.String(64), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("value", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer, nullable=True, default=0),
|
||||
sa.Column(
|
||||
"method",
|
||||
postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False),
|
||||
nullable=False,
|
||||
),
|
||||
sa.PrimaryKeyConstraint("service_id", "setting_id", "suffix"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_settings_old (service_id, setting_id, value, suffix, method)
|
||||
SELECT service_id, setting_id, value, suffix, method FROM bw_services_settings
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services_settings")
|
||||
op.rename_table("bw_services_settings_old", "bw_services_settings")
|
||||
|
||||
# Revert bw_services
|
||||
op.create_table(
|
||||
"bw_services_old",
|
||||
sa.Column("id", sa.String(64), primary_key=True),
|
||||
sa.Column("method", postgresql.ENUM("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum", create_type=False), nullable=False),
|
||||
sa.Column("is_draft", sa.Boolean, default=False, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_old (id, method, is_draft, creation_date, last_update)
|
||||
SELECT id, method, is_draft, creation_date, last_update FROM bw_services
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services")
|
||||
op.rename_table("bw_services_old", "bw_services")
|
||||
|
||||
# Recreate foreign keys referencing bw_services
|
||||
with op.batch_alter_table("bw_custom_configs") as batch_op:
|
||||
batch_op.create_foreign_key("bw_custom_configs_service_id_fkey", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.create_foreign_key("bw_services_settings_service_id_fkey", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("bw_jobs_cache_service_id_fkey", "bw_services", ["service_id"], ["id"], onupdate="CASCADE", ondelete="CASCADE")
|
||||
|
||||
# Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
"""Upgrade to version 1.5.6
|
||||
|
||||
Revision ID: b4abd1acf9f1
|
||||
Revises: 7deca2941c74
|
||||
Create Date: 2024-12-19 13:49:55.657684
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "b4abd1acf9f1"
|
||||
down_revision: Union[str, None] = "7deca2941c74"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Create enums
|
||||
op.execute("CREATE TYPE pro_status_enum AS ENUM ('active', 'invalid', 'expired', 'suspended')")
|
||||
op.execute("CREATE TYPE plugin_types_enum AS ENUM ('core', 'external', 'pro')")
|
||||
op.execute("CREATE TYPE stream_types_enum AS ENUM ('no', 'yes', 'partial')")
|
||||
|
||||
# Drop the foreign key constraint dynamically
|
||||
conn = op.get_bind()
|
||||
result = conn.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'bw_jobs_cache'::regclass
|
||||
AND confrelid = 'bw_jobs'::regclass
|
||||
AND conname LIKE '%_job_name%'
|
||||
"""
|
||||
)
|
||||
).fetchone()
|
||||
|
||||
if result:
|
||||
constraint_name = result[0]
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint(constraint_name, type_="foreignkey")
|
||||
|
||||
# Drop index dynamically if it exists
|
||||
index_result = conn.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT indexname
|
||||
FROM pg_indexes
|
||||
WHERE tablename = 'bw_jobs_cache'
|
||||
AND indexname = 'job_name'
|
||||
"""
|
||||
)
|
||||
).fetchone()
|
||||
|
||||
if index_result:
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_index("job_name")
|
||||
|
||||
# Add new foreign key
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_foreign_key("fk_bw_jobs_cache_job_name", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
# Add new columns and alter existing ones
|
||||
op.add_column("bw_metadata", sa.Column("is_pro", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_metadata", sa.Column("pro_expire", sa.DateTime(), nullable=True))
|
||||
op.add_column(
|
||||
"bw_metadata",
|
||||
sa.Column("pro_status", sa.Enum("active", "invalid", "expired", "suspended", name="pro_status_enum"), nullable=False, server_default="invalid"),
|
||||
)
|
||||
op.add_column("bw_metadata", sa.Column("pro_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
op.add_column("bw_metadata", sa.Column("pro_overlapped", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
op.add_column("bw_metadata", sa.Column("last_pro_check", sa.DateTime(), nullable=True))
|
||||
op.add_column("bw_metadata", sa.Column("pro_plugins_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
op.add_column("bw_services", sa.Column("is_draft", sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||
|
||||
op.add_column("bw_plugins", sa.Column("type", sa.Enum("core", "external", "pro", name="plugin_types_enum"), nullable=False, server_default="core"))
|
||||
|
||||
# Alter the `stream` column with explicit casting
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE bw_plugins
|
||||
ALTER COLUMN stream TYPE stream_types_enum
|
||||
USING stream::text::stream_types_enum
|
||||
"""
|
||||
)
|
||||
|
||||
# Migrate data: Set 'type' to 'external' where 'external' was true
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET type = 'external'
|
||||
WHERE external = true
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_column("bw_plugins", "external")
|
||||
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.VARCHAR(length=8192), type_=sa.TEXT(), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.VARCHAR(length=8192), type_=sa.TEXT(), existing_nullable=False)
|
||||
|
||||
# Drop indices dynamically if they exist
|
||||
for table, index_name in (("bw_jobs", "name"), ("bw_settings", "name")):
|
||||
index_exists = conn.execute(
|
||||
sa.text(
|
||||
f"""
|
||||
SELECT indexname
|
||||
FROM pg_indexes
|
||||
WHERE tablename = '{table}'
|
||||
AND indexname = '{index_name}'
|
||||
"""
|
||||
)
|
||||
).fetchone()
|
||||
|
||||
if index_exists:
|
||||
op.drop_index(index_name, table_name=table)
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET is_pro = false,
|
||||
pro_status = 'invalid',
|
||||
pro_services = 0,
|
||||
pro_overlapped = false,
|
||||
pro_plugins_changed = false,
|
||||
version = '1.5.6'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Drop columns
|
||||
op.drop_column("bw_metadata", "pro_plugins_changed")
|
||||
op.drop_column("bw_metadata", "last_pro_check")
|
||||
op.drop_column("bw_metadata", "pro_overlapped")
|
||||
op.drop_column("bw_metadata", "pro_services")
|
||||
op.drop_column("bw_metadata", "pro_status")
|
||||
op.drop_column("bw_metadata", "pro_expire")
|
||||
op.drop_column("bw_metadata", "is_pro")
|
||||
|
||||
op.drop_column("bw_services", "is_draft")
|
||||
|
||||
op.add_column("bw_plugins", sa.Column("external", sa.Boolean(), autoincrement=False, nullable=False))
|
||||
|
||||
# Migrate data: Set 'external' to true where 'type' was 'external'
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET external = true
|
||||
WHERE type = 'external'
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_column("bw_plugins", "type")
|
||||
|
||||
# Revert the `stream` column back to VARCHAR
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE bw_plugins
|
||||
ALTER COLUMN stream TYPE VARCHAR(16)
|
||||
USING stream::text
|
||||
"""
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"bw_plugins", "stream", existing_type=sa.Enum("no", "yes", "partial", name="stream_types_enum"), type_=sa.VARCHAR(length=16), existing_nullable=False
|
||||
)
|
||||
op.alter_column("bw_global_values", "value", existing_type=sa.TEXT(), type_=sa.VARCHAR(length=8192), existing_nullable=False)
|
||||
op.alter_column("bw_services_settings", "value", existing_type=sa.TEXT(), type_=sa.VARCHAR(length=8192), existing_nullable=False)
|
||||
|
||||
# Recreate indices
|
||||
op.create_index("name", "bw_jobs", ["name", "plugin_id"], unique=True)
|
||||
op.create_index("name", "bw_settings", ["name"], unique=True)
|
||||
|
||||
# Drop the enum types
|
||||
op.execute("DROP TYPE IF EXISTS pro_status_enum")
|
||||
op.execute("DROP TYPE IF EXISTS plugin_types_enum")
|
||||
op.execute("DROP TYPE IF EXISTS stream_types_enum")
|
||||
|
||||
# Recreate foreign key dynamically
|
||||
result = (
|
||||
op.get_bind()
|
||||
.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'bw_jobs_cache'::regclass
|
||||
AND conname = 'fk_bw_jobs_cache_job_name'
|
||||
"""
|
||||
)
|
||||
)
|
||||
.fetchone()
|
||||
)
|
||||
|
||||
if result:
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.drop_constraint("fk_bw_jobs_cache_job_name", type_="foreignkey")
|
||||
|
||||
with op.batch_alter_table("bw_jobs_cache") as batch_op:
|
||||
batch_op.create_index("job_name", ["job_name", "service_id", "file_name"], unique=True)
|
||||
batch_op.create_foreign_key("bw_jobs_cache_ibfk_1", "bw_jobs", ["job_name"], ["name"])
|
||||
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
"""Upgrade to version 1.5.8
|
||||
|
||||
Revision ID: ba43081c6f96
|
||||
Revises: 5201c88f004d
|
||||
Create Date: 2024-12-19 14:40:08.719321
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "ba43081c6f96"
|
||||
down_revision: Union[str, None] = "5201c88f004d"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add new columns to 'bw_metadata'
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("pro_license", sa.String(length=128), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_custom_configs_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_external_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_pro_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_instances_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("failover", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Add new columns to 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_config_change", sa.DateTime(), nullable=True))
|
||||
|
||||
# Set default value for 'config_changed' in 'bw_plugins'
|
||||
op.execute("UPDATE bw_plugins SET config_changed = false")
|
||||
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert 'bw_plugins' changes
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("last_config_change")
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Revert 'bw_metadata' changes
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("failover")
|
||||
batch_op.drop_column("last_instances_change")
|
||||
batch_op.drop_column("last_pro_plugins_change")
|
||||
batch_op.drop_column("last_external_plugins_change")
|
||||
batch_op.drop_column("last_custom_configs_change")
|
||||
batch_op.drop_column("pro_license")
|
||||
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7', config_changed = false WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.3
|
||||
|
||||
Revision ID: e9b703e3d747
|
||||
Revises: 4a2457daed53
|
||||
Create Date: 2024-12-19 13:41:11.126097
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "e9b703e3d747"
|
||||
down_revision: Union[str, None] = "4a2457daed53"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.3
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
"""Upgrade to version 1.5.11
|
||||
|
||||
Revision ID: efb577b1c25d
|
||||
Revises: 0a2e336b02e7
|
||||
Create Date: 2024-12-19 14:42:50.054087
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "efb577b1c25d"
|
||||
down_revision: Union[str, None] = "0a2e336b02e7"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add the new non_draft_services column to bw_metadata
|
||||
op.add_column("bw_metadata", sa.Column("non_draft_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
|
||||
# Update the version in metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Reverse the addition of the non_draft_services column
|
||||
op.drop_column("bw_metadata", "non_draft_services")
|
||||
|
||||
# Revert version to 1.5.10
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.12
|
||||
|
||||
Revision ID: fbd680c6ffeb
|
||||
Revises: efb577b1c25d
|
||||
Create Date: 2024-12-19 14:46:13.313028
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "fbd680c6ffeb"
|
||||
down_revision: Union[str, None] = "efb577b1c25d"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version to 1.5.12
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version to 1.5.11
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.9
|
||||
|
||||
Revision ID: fe047f892d6b
|
||||
Revises: ba43081c6f96
|
||||
Create Date: 2024-12-19 14:41:12.553100
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "fe047f892d6b"
|
||||
down_revision: Union[str, None] = "ba43081c6f96"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
26
src/common/db/alembic/script.py.mako
Normal file
26
src/common/db/alembic/script.py.mako
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = ${repr(up_revision)}
|
||||
down_revision: Union[str, None] = ${repr(down_revision)}
|
||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
"""Upgrade to version 1.5.6
|
||||
|
||||
Revision ID: 0a4144dd55d4
|
||||
Revises: c9586782cd77
|
||||
Create Date: 2024-12-17 08:39:34.882278
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0a4144dd55d4"
|
||||
down_revision: Union[str, None] = "c9586782cd77"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
# Define Enums for consistency
|
||||
PRO_STATUS_ENUM = sa.Enum("active", "invalid", "expired", "suspended", name="pro_status_enum")
|
||||
PLUGIN_TYPES_ENUM = sa.Enum("core", "external", "pro", name="plugin_types_enum")
|
||||
STREAM_TYPES_ENUM = sa.Enum("no", "yes", "partial", name="stream_types_enum")
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Alter value columns to TEXT in bw_global_values and bw_services_settings
|
||||
with op.batch_alter_table("bw_global_values") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.TEXT(), existing_type=sa.VARCHAR(length=8192))
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.TEXT(), existing_type=sa.VARCHAR(length=8192))
|
||||
|
||||
# Add new columns to bw_metadata
|
||||
op.add_column("bw_metadata", sa.Column("is_pro", sa.Boolean(), nullable=False, server_default="0"))
|
||||
op.add_column("bw_metadata", sa.Column("pro_expire", sa.DateTime(), nullable=True))
|
||||
op.add_column("bw_metadata", sa.Column("pro_status", PRO_STATUS_ENUM, nullable=False, server_default="invalid"))
|
||||
op.add_column("bw_metadata", sa.Column("pro_services", sa.Integer(), nullable=False, server_default="0"))
|
||||
op.add_column("bw_metadata", sa.Column("pro_overlapped", sa.Boolean(), nullable=False, server_default="0"))
|
||||
op.add_column("bw_metadata", sa.Column("last_pro_check", sa.DateTime(), nullable=True))
|
||||
op.add_column("bw_metadata", sa.Column("pro_plugins_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
# Modify bw_plugins table
|
||||
# Step 1: Add the new 'type' column with default 'external'
|
||||
op.add_column("bw_plugins", sa.Column("type", PLUGIN_TYPES_ENUM, nullable=False, server_default="core"))
|
||||
|
||||
# Step 2: Migrate data: Set 'type' to 'external' where 'external' was true
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET type = 'external'
|
||||
WHERE external = true
|
||||
"""
|
||||
)
|
||||
|
||||
# Step 3: Drop the 'external' column and alter the 'stream' column to STREAM_TYPES_ENUM
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("external")
|
||||
batch_op.alter_column("stream", type_=STREAM_TYPES_ENUM, existing_type=sa.VARCHAR(length=16))
|
||||
|
||||
# Add is_draft column to bw_services
|
||||
op.add_column("bw_services", sa.Column("is_draft", sa.Boolean(), nullable=False, server_default="0"))
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET is_pro = false,
|
||||
pro_status = 'invalid',
|
||||
pro_services = 0,
|
||||
pro_overlapped = false,
|
||||
pro_plugins_changed = false,
|
||||
version = '1.5.6'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert changes in bw_services
|
||||
op.drop_column("bw_services", "is_draft")
|
||||
|
||||
# Revert changes in bw_plugins
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("external", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.alter_column("stream", type_=sa.VARCHAR(length=16), existing_type=STREAM_TYPES_ENUM)
|
||||
|
||||
# Migrate data: Set 'type' to 'external' where 'external' was true
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_plugins
|
||||
SET external = true
|
||||
WHERE type = 'external'
|
||||
"""
|
||||
)
|
||||
|
||||
# Drop new columns from bw_plugins
|
||||
op.drop_column("bw_plugins", "type")
|
||||
|
||||
# Drop new columns from bw_metadata
|
||||
op.drop_column("bw_metadata", "pro_plugins_changed")
|
||||
op.drop_column("bw_metadata", "last_pro_check")
|
||||
op.drop_column("bw_metadata", "pro_overlapped")
|
||||
op.drop_column("bw_metadata", "pro_services")
|
||||
op.drop_column("bw_metadata", "pro_status")
|
||||
op.drop_column("bw_metadata", "pro_expire")
|
||||
op.drop_column("bw_metadata", "is_pro")
|
||||
|
||||
# Revert value columns in bw_global_values and bw_services_settings
|
||||
with op.batch_alter_table("bw_global_values") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.VARCHAR(length=8192), existing_type=sa.TEXT())
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.VARCHAR(length=8192), existing_type=sa.TEXT())
|
||||
|
||||
# Revert version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
"""Upgrade to version 1.5.8
|
||||
|
||||
Revision ID: 13fb1f986f11
|
||||
Revises: 91859f8f75ad
|
||||
Create Date: 2024-12-17 08:41:34.664880
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "13fb1f986f11"
|
||||
down_revision: Union[str, None] = "91859f8f75ad"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add new columns to 'bw_metadata'
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("pro_license", sa.String(length=128), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_custom_configs_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_external_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_pro_plugins_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_instances_change", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("failover", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Add new columns to 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_config_change", sa.DateTime(), nullable=True))
|
||||
|
||||
# Set default value for 'config_changed' in 'bw_plugins'
|
||||
op.execute("UPDATE bw_plugins SET config_changed = false")
|
||||
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert 'bw_plugins' changes
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("last_config_change")
|
||||
batch_op.drop_column("config_changed")
|
||||
|
||||
# Revert 'bw_metadata' changes
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("failover")
|
||||
batch_op.drop_column("last_instances_change")
|
||||
batch_op.drop_column("last_pro_plugins_change")
|
||||
batch_op.drop_column("last_external_plugins_change")
|
||||
batch_op.drop_column("last_custom_configs_change")
|
||||
batch_op.drop_column("pro_license")
|
||||
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7', config_changed = false WHERE id = 1")
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
"""Upgrade to version 1.5.4
|
||||
|
||||
Revision ID: 17a6fddfddc2
|
||||
Revises: eb3ca0f3f20c
|
||||
Create Date: 2024-12-17 08:38:06.860342
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "17a6fddfddc2"
|
||||
down_revision: Union[str, None] = "eb3ca0f3f20c"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create bw_ui_users table
|
||||
op.create_table(
|
||||
"bw_ui_users",
|
||||
sa.Column("id", sa.Integer(), primary_key=True, nullable=False),
|
||||
sa.Column("username", sa.String(length=256), nullable=False, unique=True),
|
||||
sa.Column("password", sa.String(length=60), nullable=False),
|
||||
)
|
||||
|
||||
# Update metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop bw_ui_users table
|
||||
op.drop_table("bw_ui_users")
|
||||
|
||||
# Revert metadata version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,502 @@
|
|||
"""Upgrade to version 1.6.0-beta
|
||||
|
||||
Revision ID: 1e1fc017a424
|
||||
Revises: 75a5d34f9a7d
|
||||
Create Date: 2024-12-17 08:42:34.116054
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "1e1fc017a424"
|
||||
down_revision: Union[str, None] = "75a5d34f9a7d"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# --- Enums Changes ---
|
||||
# METHODS_ENUM now includes "wizard"
|
||||
# CUSTOM_CONFIGS_TYPES_ENUM now includes "default_server_stream", "crs_plugins_before", "crs_plugins_after"
|
||||
# PLUGIN_TYPES_ENUM now includes "ui"
|
||||
# INSTANCE_TYPE_ENUM and INSTANCE_STATUS_ENUM are new
|
||||
# These changes do not directly translate to SQLite schema changes since SQLite doesn't enforce enum types.
|
||||
# The application code will handle the new enum values. No direct DDL required for SQLite.
|
||||
|
||||
# --- New Tables for UI ---
|
||||
op.create_table(
|
||||
"bw_ui_permissions",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles",
|
||||
sa.Column("name", sa.String(64), primary_key=True),
|
||||
sa.Column("description", sa.String(256), nullable=False),
|
||||
sa.Column("update_datetime", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_recovery_codes",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("code", sa.UnicodeText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_permissions",
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.Column("permission_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["permission_name"], ["bw_ui_permissions.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("role_name", "permission_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_roles_users",
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("role_name", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["role_name"], ["bw_ui_roles.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("user_name", "role_name"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_sessions",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("ip", sa.String(39), nullable=False),
|
||||
sa.Column("user_agent", sa.TEXT, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_activity", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
from model import JSONText
|
||||
|
||||
op.create_table(
|
||||
"bw_ui_user_columns_preferences",
|
||||
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column("user_name", sa.String(256), nullable=False),
|
||||
sa.Column("table_name", sa.Enum("bans", "configs", "instances", "jobs", "plugins", "reports", "services", name="tables_enum"), nullable=False),
|
||||
sa.Column("columns", JSONText, nullable=False),
|
||||
sa.ForeignKeyConstraint(["user_name"], ["bw_ui_users.username"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("user_name", "table_name", name="uq_user_columns_preferences"),
|
||||
)
|
||||
|
||||
# --- Templates Tables ---
|
||||
op.create_table(
|
||||
"bw_templates",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("name", sa.String(256), unique=True, nullable=False),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_steps",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False, primary_key=True),
|
||||
sa.Column("title", sa.TEXT, nullable=False),
|
||||
sa.Column("subtitle", sa.TEXT, nullable=True),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id", "template_id"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_settings",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column("default", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer(), nullable=True, default=0),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["setting_id"], ["bw_settings.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "setting_id", "step_id", "suffix"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"bw_template_custom_configs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("template_id", sa.String(256), nullable=False),
|
||||
sa.Column("step_id", sa.Integer(), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.ForeignKeyConstraint(["template_id"], ["bw_templates.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.UniqueConstraint("template_id", "step_id", "type", "name"),
|
||||
)
|
||||
|
||||
# Create a new table with the updated custom_configs schema
|
||||
op.create_table(
|
||||
"bw_custom_configs_new",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("service_id", sa.String(64), sa.ForeignKey("bw_services.id", onupdate="cascade", ondelete="cascade"), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.Column(
|
||||
"method",
|
||||
sa.Enum(
|
||||
"ui",
|
||||
"scheduler",
|
||||
"autoconf",
|
||||
"manual",
|
||||
"wizard",
|
||||
name="methods_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.UniqueConstraint("service_id", "type", "name"),
|
||||
)
|
||||
|
||||
# Copy data from the old table to the new table
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_custom_configs_new (id, service_id, type, name, data, checksum, method)
|
||||
SELECT id, service_id, type, name, data, checksum, method FROM bw_custom_configs
|
||||
"""
|
||||
)
|
||||
|
||||
# Drop the old table
|
||||
op.drop_table("bw_custom_configs")
|
||||
|
||||
# Rename the new table to match the original table's name
|
||||
op.rename_table("bw_custom_configs_new", "bw_custom_configs")
|
||||
|
||||
# Create a new bw_settings table with updated schema (only 'id' as PK and 'name' as unique)
|
||||
op.create_table(
|
||||
"bw_settings_new",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("name", sa.String(256), unique=True, nullable=False),
|
||||
sa.Column("plugin_id", sa.String(64), sa.ForeignKey("bw_plugins.id", onupdate="cascade", ondelete="cascade"), nullable=False),
|
||||
sa.Column("context", sa.Enum("global", "multisite", name="contexts_enum"), nullable=False),
|
||||
sa.Column("default", sa.TEXT, nullable=True),
|
||||
sa.Column("help", sa.String(512), nullable=False),
|
||||
sa.Column("label", sa.String(256), nullable=True),
|
||||
sa.Column("regex", sa.String(1024), nullable=False),
|
||||
sa.Column("type", sa.Enum("password", "text", "check", "select", name="settings_types_enum"), nullable=False),
|
||||
sa.Column("multiple", sa.String(128), nullable=True),
|
||||
sa.Column("order", sa.Integer, default=0, nullable=False),
|
||||
)
|
||||
|
||||
# Copy data from the old table to the new table
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_settings_new (id, name, plugin_id, context, "default", help, label, regex, type, multiple, "order")
|
||||
SELECT id, name, plugin_id, context, "default", help, label, regex, type, multiple, "order"
|
||||
FROM bw_settings
|
||||
"""
|
||||
)
|
||||
|
||||
# Drop the old table
|
||||
op.drop_table("bw_settings")
|
||||
|
||||
# Rename the new table to match the original name
|
||||
op.rename_table("bw_settings_new", "bw_settings")
|
||||
|
||||
# --- bw_jobs table changes ---
|
||||
# Add run_async column, drop success and last_run
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.add_column(sa.Column("run_async", sa.Boolean(), nullable=True, server_default="0"))
|
||||
# Remove old columns success, last_run
|
||||
batch_op.drop_column("success")
|
||||
batch_op.drop_column("last_run")
|
||||
# set run_async to not nullable now that we have a default
|
||||
op.execute("UPDATE bw_jobs SET run_async = 0 WHERE run_async IS NULL")
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.alter_column("run_async", nullable=False)
|
||||
|
||||
# New bw_jobs_runs table
|
||||
op.create_table(
|
||||
"bw_jobs_runs",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("success", sa.Boolean(), nullable=True, server_default="0"),
|
||||
sa.Column("start_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("end_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["job_name"], ["bw_jobs.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
# --- bw_services changes ---
|
||||
# Add creation_date, last_update
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_update", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
# Set current timestamp for existing services
|
||||
op.execute("UPDATE bw_services SET creation_date = CURRENT_TIMESTAMP, last_update = CURRENT_TIMESTAMP")
|
||||
|
||||
# Now make them non-nullable
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.alter_column("creation_date", nullable=False)
|
||||
batch_op.alter_column("last_update", nullable=False)
|
||||
|
||||
# --- bw_ui_users changes ---
|
||||
# Create a temporary new table
|
||||
op.create_table(
|
||||
"bw_ui_users_new",
|
||||
sa.Column("username", sa.String(256), primary_key=True),
|
||||
sa.Column("email", sa.String(256), nullable=True, unique=True),
|
||||
sa.Column("password", sa.String(60), nullable=False),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=False, server_default="manual"),
|
||||
sa.Column("admin", sa.Boolean(), nullable=False, server_default="0"),
|
||||
sa.Column("theme", sa.Enum("light", "dark", name="themes_enum"), nullable=False, server_default="light"),
|
||||
sa.Column("totp_secret", sa.String(256), nullable=True),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp()),
|
||||
sa.Column("update_date", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp()),
|
||||
)
|
||||
|
||||
# Migrate data from old bw_ui_users
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_ui_users_new (username, password, method, admin, theme, creation_date, update_date)
|
||||
SELECT username, password, method, 1, 'light', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
||||
FROM bw_ui_users
|
||||
"""
|
||||
)
|
||||
|
||||
# Drop old table and rename new table
|
||||
op.drop_table("bw_ui_users")
|
||||
op.rename_table("bw_ui_users_new", "bw_ui_users")
|
||||
|
||||
# --- bw_plugin_pages changes ---
|
||||
# Old had template_file, template_checksum, actions_file, actions_checksum, obfuscation_file, obfuscation_checksum
|
||||
# New has id, plugin_id(unique), data, checksum
|
||||
op.create_table(
|
||||
"bw_plugin_pages_new",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("data", sa.LargeBinary(length=4294967295), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_plugin_pages_new (id, plugin_id, data, checksum)
|
||||
SELECT id, plugin_id, template_file, '' FROM bw_plugin_pages
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_plugin_pages")
|
||||
op.rename_table("bw_plugin_pages_new", "bw_plugin_pages")
|
||||
|
||||
# --- bw_instances changes ---
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.add_column(sa.Column("name", sa.String(256), nullable=True))
|
||||
batch_op.add_column(sa.Column("type", sa.Enum("static", "container", "pod", name="instance_type_enum"), nullable=True))
|
||||
batch_op.add_column(sa.Column("status", sa.Enum("loading", "up", "down", name="instance_status_enum"), nullable=True))
|
||||
batch_op.add_column(sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=True))
|
||||
batch_op.add_column(sa.Column("creation_date", sa.DateTime(timezone=True), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_seen", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
op.execute("DELETE FROM bw_instances WHERE name IS NULL")
|
||||
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.alter_column("name", nullable=False)
|
||||
batch_op.alter_column("type", nullable=False)
|
||||
batch_op.alter_column("status", nullable=False)
|
||||
batch_op.alter_column("method", nullable=False)
|
||||
batch_op.alter_column("creation_date", nullable=False)
|
||||
batch_op.alter_column("last_seen", nullable=False)
|
||||
|
||||
# Update version to 1.6.0-beta
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Downgrade steps: revert all changes that can't be easily undone.
|
||||
# This will drop newly created tables and attempt to restore old structure.
|
||||
|
||||
# Drop new tables
|
||||
op.drop_table("bw_template_custom_configs")
|
||||
op.drop_table("bw_template_settings")
|
||||
op.drop_table("bw_template_steps")
|
||||
op.drop_table("bw_templates")
|
||||
op.drop_table("bw_jobs_runs")
|
||||
op.drop_table("bw_ui_user_sessions")
|
||||
op.drop_table("bw_ui_roles_users")
|
||||
op.drop_table("bw_ui_roles_permissions")
|
||||
op.drop_table("bw_ui_user_recovery_codes")
|
||||
op.drop_table("bw_ui_user_columns_preferences")
|
||||
op.drop_table("bw_ui_roles")
|
||||
op.drop_table("bw_ui_permissions")
|
||||
|
||||
# Revert bw_plugin_pages
|
||||
op.create_table(
|
||||
"bw_plugin_pages_old",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("plugin_id", sa.String(64), nullable=False),
|
||||
sa.Column("template_file", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("template_checksum", sa.String(128), nullable=False),
|
||||
sa.Column("actions_file", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("actions_checksum", sa.String(128), nullable=False),
|
||||
sa.Column("obfuscation_file", sa.LargeBinary(length=(2**32) - 1), nullable=True),
|
||||
sa.Column("obfuscation_checksum", sa.String(128), nullable=True),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
op.drop_table("bw_plugin_pages")
|
||||
op.rename_table("bw_plugin_pages_old", "bw_plugin_pages")
|
||||
|
||||
# bw_jobs revert
|
||||
with op.batch_alter_table("bw_jobs") as batch_op:
|
||||
batch_op.add_column(sa.Column("success", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("last_run", sa.DateTime(), nullable=True))
|
||||
batch_op.drop_column("run_async")
|
||||
|
||||
# bw_services revert
|
||||
with op.batch_alter_table("bw_services") as batch_op:
|
||||
batch_op.drop_column("last_update")
|
||||
batch_op.drop_column("creation_date")
|
||||
|
||||
# bw_instances revert
|
||||
with op.batch_alter_table("bw_instances") as batch_op:
|
||||
batch_op.drop_column("last_seen")
|
||||
batch_op.drop_column("creation_date")
|
||||
batch_op.drop_column("method")
|
||||
batch_op.drop_column("status")
|
||||
batch_op.drop_column("type")
|
||||
batch_op.drop_column("name")
|
||||
|
||||
# Revert custom configs
|
||||
op.create_table(
|
||||
"bw_custom_configs_old",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("service_id", sa.String(64), sa.ForeignKey("bw_services.id", onupdate="cascade", ondelete="cascade"), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
sa.Enum(
|
||||
"http",
|
||||
"default_server_http",
|
||||
"server_http",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"stream",
|
||||
"server_stream",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.Column(
|
||||
"method",
|
||||
sa.Enum(
|
||||
"ui",
|
||||
"scheduler",
|
||||
"autoconf",
|
||||
"manual",
|
||||
name="methods_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.UniqueConstraint("service_id", "type", "name"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_custom_configs_old (id, service_id, type, name, data, checksum, method)
|
||||
SELECT id, service_id, type, name, data, checksum, method
|
||||
FROM bw_custom_configs
|
||||
WHERE type IN ('http', 'default_server_http', 'server_http', 'modsec', 'modsec_crs', 'stream', 'server_stream')
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_custom_configs")
|
||||
op.rename_table("bw_custom_configs_old", "bw_custom_configs")
|
||||
|
||||
# Revert bw_settings
|
||||
op.create_table(
|
||||
"bw_settings_old",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("name", sa.String(256), primary_key=True),
|
||||
sa.Column("plugin_id", sa.String(64), sa.ForeignKey("bw_plugins.id", onupdate="cascade", ondelete="cascade"), nullable=False),
|
||||
sa.Column("context", sa.Enum("global", "multisite", name="contexts_enum"), nullable=False),
|
||||
sa.Column("default", sa.String(4096), nullable=True, default=""),
|
||||
sa.Column("help", sa.String(512), nullable=False),
|
||||
sa.Column("label", sa.String(256), nullable=True),
|
||||
sa.Column("regex", sa.String(1024), nullable=False),
|
||||
sa.Column("type", sa.Enum("password", "text", "check", "select", name="settings_types_enum"), nullable=False),
|
||||
sa.Column("multiple", sa.String(128), nullable=True),
|
||||
sa.Column("order", sa.Integer, default=0, nullable=False),
|
||||
sa.PrimaryKeyConstraint("id", "name"),
|
||||
sa.UniqueConstraint("id"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_settings_old (id, name, plugin_id, context, "default", help, label, regex, type, multiple, "order")
|
||||
SELECT id, name, plugin_id, context, "default", help, label, regex, type, multiple, "order"
|
||||
FROM bw_settings
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_settings")
|
||||
op.rename_table("bw_settings_old", "bw_settings")
|
||||
|
||||
# bw_ui_users revert
|
||||
op.create_table(
|
||||
"bw_ui_users_old",
|
||||
sa.Column("id", sa.Integer, primary_key=True, default=1),
|
||||
sa.Column("username", sa.String(256), nullable=False, unique=True),
|
||||
sa.Column("password", sa.String(60), nullable=False),
|
||||
sa.Column("is_two_factor_enabled", sa.Boolean, nullable=False, default=False),
|
||||
sa.Column("secret_token", sa.String(32), nullable=True, unique=True, default=None),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum"), nullable=False, default="manual"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_ui_users_old (username, password, method)
|
||||
SELECT username, password, method FROM bw_ui_users
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_ui_users")
|
||||
op.rename_table("bw_ui_users_old", "bw_ui_users")
|
||||
|
||||
# Revert version
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
"""Upgrade to version 1.5.2
|
||||
|
||||
Revision ID: 259e352699f1
|
||||
Revises: 6599b34870d1
|
||||
Create Date: 2024-12-17 08:38:02.818323
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "259e352699f1"
|
||||
down_revision: Union[str, None] = "6599b34870d1"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.1
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.1' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.9
|
||||
|
||||
Revision ID: 4f7bdc32c662
|
||||
Revises: 13fb1f986f11
|
||||
Create Date: 2024-12-17 08:41:37.205928
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "4f7bdc32c662"
|
||||
down_revision: Union[str, None] = "13fb1f986f11"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.8' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
"""Upgrade to version 1.6.0-rc1
|
||||
|
||||
Revision ID: 5b0ea031ccfc
|
||||
Revises: 1e1fc017a424
|
||||
Create Date: 2024-12-20 08:36:31.739835
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "5b0ea031ccfc"
|
||||
down_revision: Union[str, None] = "1e1fc017a424"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Enable foreign key checks (SQLite specific)
|
||||
op.execute("PRAGMA foreign_keys=OFF;")
|
||||
|
||||
# --- bw_services ---
|
||||
# Old schema (1.6.0-beta):
|
||||
# id: String(64), method: METHODS_ENUM, is_draft: Boolean, creation_date: DateTime, last_update: DateTime
|
||||
# New schema (1.6.0-rc1):
|
||||
# id: String(256), (other columns unchanged)
|
||||
op.create_table(
|
||||
"bw_services_new",
|
||||
sa.Column("id", sa.String(256), primary_key=True),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=False),
|
||||
sa.Column("is_draft", sa.Boolean, default=False, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
# Copy data from old bw_services to bw_services_new
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_new (id, method, is_draft, creation_date, last_update)
|
||||
SELECT id, method, is_draft, creation_date, last_update FROM bw_services
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services")
|
||||
op.rename_table("bw_services_new", "bw_services")
|
||||
|
||||
# --- bw_services_settings ---
|
||||
# Old schema:
|
||||
# service_id: String(64) FK -> bw_services.id
|
||||
# New schema:
|
||||
# service_id: String(256) FK -> bw_services.id
|
||||
op.create_table(
|
||||
"bw_services_settings_new",
|
||||
sa.Column("service_id", sa.String(256), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("value", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer, nullable=True, default=0),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=False),
|
||||
sa.PrimaryKeyConstraint("service_id", "setting_id", "suffix"),
|
||||
sa.ForeignKeyConstraint(["service_id"], ["bw_services.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["setting_id"], ["bw_settings.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_settings_new (service_id, setting_id, value, suffix, method)
|
||||
SELECT service_id, setting_id, value, suffix, method
|
||||
FROM bw_services_settings
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services_settings")
|
||||
op.rename_table("bw_services_settings_new", "bw_services_settings")
|
||||
|
||||
# --- bw_custom_configs ---
|
||||
# Old schema:
|
||||
# service_id: String(64)
|
||||
# New schema:
|
||||
# service_id: String(256)
|
||||
op.create_table(
|
||||
"bw_custom_configs_new",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("service_id", sa.String(256), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=False),
|
||||
sa.UniqueConstraint("service_id", "type", "name"),
|
||||
sa.ForeignKeyConstraint(["service_id"], ["bw_services.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_custom_configs_new (id, service_id, type, name, data, checksum, method)
|
||||
SELECT id, service_id, type, name, data, checksum, method FROM bw_custom_configs
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_custom_configs")
|
||||
op.rename_table("bw_custom_configs_new", "bw_custom_configs")
|
||||
|
||||
# --- bw_jobs_cache ---
|
||||
# Old schema:
|
||||
# service_id: String(64)
|
||||
# New schema:
|
||||
# service_id: String(256)
|
||||
op.create_table(
|
||||
"bw_jobs_cache_new",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("service_id", sa.String(256), nullable=True),
|
||||
sa.Column("file_name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=True),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("checksum", sa.String(128), nullable=True),
|
||||
sa.ForeignKeyConstraint(["job_name"], ["bw_jobs.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["service_id"], ["bw_services.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_jobs_cache_new (id, job_name, service_id, file_name, data, last_update, checksum)
|
||||
SELECT id, job_name, service_id, file_name, data, last_update, checksum FROM bw_jobs_cache
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_jobs_cache")
|
||||
op.rename_table("bw_jobs_cache_new", "bw_jobs_cache")
|
||||
|
||||
# Re-enable foreign keys
|
||||
op.execute("PRAGMA foreign_keys=ON;")
|
||||
|
||||
# Update the version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-rc1' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Disable foreign keys to allow dropping and recreating tables
|
||||
op.execute("PRAGMA foreign_keys=OFF;")
|
||||
|
||||
# --- bw_services ---
|
||||
# Downgrade: revert id from VARCHAR(256) back to VARCHAR(64)
|
||||
op.create_table(
|
||||
"bw_services_old",
|
||||
sa.Column("id", sa.String(64), primary_key=True),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=False),
|
||||
sa.Column("is_draft", sa.Boolean, default=False, nullable=False),
|
||||
sa.Column("creation_date", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=False),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_old (id, method, is_draft, creation_date, last_update)
|
||||
SELECT id, method, is_draft, creation_date, last_update FROM bw_services
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services")
|
||||
op.rename_table("bw_services_old", "bw_services")
|
||||
|
||||
# --- bw_services_settings ---
|
||||
# Downgrade: revert service_id from VARCHAR(256) back to VARCHAR(64)
|
||||
op.create_table(
|
||||
"bw_services_settings_old",
|
||||
sa.Column("service_id", sa.String(64), nullable=False),
|
||||
sa.Column("setting_id", sa.String(256), nullable=False),
|
||||
sa.Column("value", sa.TEXT, nullable=False),
|
||||
sa.Column("suffix", sa.Integer, nullable=True, default=0),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum"), nullable=False),
|
||||
sa.PrimaryKeyConstraint("service_id", "setting_id", "suffix"),
|
||||
sa.ForeignKeyConstraint(["service_id"], ["bw_services.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["setting_id"], ["bw_settings.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_services_settings_old (service_id, setting_id, value, suffix, method)
|
||||
SELECT service_id, setting_id, value, suffix, method FROM bw_services_settings
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_services_settings")
|
||||
op.rename_table("bw_services_settings_old", "bw_services_settings")
|
||||
|
||||
# --- bw_custom_configs ---
|
||||
# Downgrade: revert service_id from VARCHAR(256) back to VARCHAR(64)
|
||||
op.create_table(
|
||||
"bw_custom_configs_old",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("service_id", sa.String(64), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
sa.Enum(
|
||||
"http",
|
||||
"stream",
|
||||
"server_http",
|
||||
"server_stream",
|
||||
"default_server_http",
|
||||
"default_server_stream",
|
||||
"modsec",
|
||||
"modsec_crs",
|
||||
"crs_plugins_before",
|
||||
"crs_plugins_after",
|
||||
name="custom_configs_types_enum",
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=False),
|
||||
sa.Column("checksum", sa.String(128), nullable=False),
|
||||
sa.Column("method", sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum"), nullable=False),
|
||||
sa.UniqueConstraint("service_id", "type", "name"),
|
||||
sa.ForeignKeyConstraint(["service_id"], ["bw_services.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_custom_configs_old (id, service_id, type, name, data, checksum, method)
|
||||
SELECT id, service_id, type, name, data, checksum, method FROM bw_custom_configs
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_custom_configs")
|
||||
op.rename_table("bw_custom_configs_old", "bw_custom_configs")
|
||||
|
||||
# --- bw_jobs_cache ---
|
||||
# Downgrade: revert service_id from VARCHAR(256) back to VARCHAR(64)
|
||||
op.create_table(
|
||||
"bw_jobs_cache_old",
|
||||
sa.Column("id", sa.Integer, sa.Identity(start=1, increment=1), primary_key=True),
|
||||
sa.Column("job_name", sa.String(128), nullable=False),
|
||||
sa.Column("service_id", sa.String(64), nullable=True),
|
||||
sa.Column("file_name", sa.String(256), nullable=False),
|
||||
sa.Column("data", sa.LargeBinary(length=(2**32) - 1), nullable=True),
|
||||
sa.Column("last_update", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("checksum", sa.String(128), nullable=True),
|
||||
sa.ForeignKeyConstraint(["job_name"], ["bw_jobs.name"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["service_id"], ["bw_services.id"], onupdate="CASCADE", ondelete="CASCADE"),
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO bw_jobs_cache_old (id, job_name, service_id, file_name, data, last_update, checksum)
|
||||
SELECT id, job_name, service_id, file_name, data, last_update, checksum FROM bw_jobs_cache
|
||||
"""
|
||||
)
|
||||
|
||||
op.drop_table("bw_jobs_cache")
|
||||
op.rename_table("bw_jobs_cache_old", "bw_jobs_cache")
|
||||
|
||||
# Re-enable foreign keys
|
||||
op.execute("PRAGMA foreign_keys=ON;")
|
||||
|
||||
# Revert the version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.6.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Upgrade to version 1.5.1
|
||||
|
||||
Revision ID: 6599b34870d1
|
||||
Revises: 8bb3be426524
|
||||
Create Date: 2024-12-17 08:38:00.674169
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "6599b34870d1"
|
||||
down_revision: Union[str, None] = "8bb3be426524"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Add new columns to bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("scheduler_first_start", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("custom_configs_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("external_plugins_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("config_changed", sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column("instances_changed", sa.Boolean(), nullable=True))
|
||||
|
||||
# Update all new columns and version in a single statement
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE bw_metadata
|
||||
SET scheduler_first_start = false,
|
||||
custom_configs_changed = false,
|
||||
external_plugins_changed = false,
|
||||
config_changed = false,
|
||||
instances_changed = false,
|
||||
version = '1.5.1'
|
||||
WHERE id = 1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Remove new columns from bw_metadata."""
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.drop_column("instances_changed")
|
||||
batch_op.drop_column("config_changed")
|
||||
batch_op.drop_column("external_plugins_changed")
|
||||
batch_op.drop_column("custom_configs_changed")
|
||||
batch_op.drop_column("scheduler_first_start")
|
||||
|
||||
# Revert the version back to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.12
|
||||
|
||||
Revision ID: 75a5d34f9a7d
|
||||
Revises: c272a8c3979c
|
||||
Create Date: 2024-12-17 08:42:31.548453
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "75a5d34f9a7d"
|
||||
down_revision: Union[str, None] = "c272a8c3979c"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version to 1.5.12
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.12' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version to 1.5.11
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"""Upgrade to version 1.5.10
|
||||
|
||||
Revision ID: 760f95e8bee7
|
||||
Revises: 4f7bdc32c662
|
||||
Create Date: 2024-12-17 08:41:39.621642
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "760f95e8bee7"
|
||||
down_revision: Union[str, None] = "4f7bdc32c662"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Update version in 'bw_metadata'
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert version in 'bw_metadata' back to 1.5.9
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.9' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
"""Upgrade to version 1.5.0
|
||||
|
||||
Revision ID: 8bb3be426524
|
||||
Revises:
|
||||
Create Date: 2024-12-17 08:35:24.898488
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "8bb3be426524"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Step 1: Drop 'order' column from 'bw_plugins'
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.drop_column("order")
|
||||
|
||||
# Data migration: Update the version to 1.5.0
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Step 1: Add 'order' column back to 'bw_plugins' (with a default value of 0 for NOT NULL constraint)
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.add_column(sa.Column("order", sa.Integer(), nullable=True, server_default="0"))
|
||||
|
||||
# Step 2: Set default value for existing rows
|
||||
op.execute("UPDATE bw_plugins SET `order` = 0")
|
||||
|
||||
# Step 3: Alter 'order' column to NOT NULL
|
||||
with op.batch_alter_table("bw_plugins") as batch_op:
|
||||
batch_op.alter_column("order", nullable=False)
|
||||
|
||||
# Data migration: Update the version to 1.5.0-beta
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.0-beta' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
"""Upgrade to version 1.5.7
|
||||
|
||||
Revision ID: 91859f8f75ad
|
||||
Revises: 0a4144dd55d4
|
||||
Create Date: 2024-12-17 08:40:26.598223
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "91859f8f75ad"
|
||||
down_revision: Union[str, None] = "0a4144dd55d4"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Step 1: Add 'order' column as nullable with a default
|
||||
with op.batch_alter_table("bw_settings") as batch_op:
|
||||
batch_op.add_column(sa.Column("order", sa.Integer(), nullable=True, server_default="0"))
|
||||
|
||||
# Step 2: Set default value for existing rows
|
||||
op.execute("UPDATE bw_settings SET `order` = 0")
|
||||
|
||||
# Step 3: Alter 'order' column to NOT NULL
|
||||
with op.batch_alter_table("bw_settings") as batch_op:
|
||||
batch_op.alter_column("order", nullable=False)
|
||||
|
||||
# Add new columns to 'bw_plugin_pages'
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.add_column(sa.Column("obfuscation_file", sa.LargeBinary(length=4294967295), nullable=True))
|
||||
batch_op.add_column(sa.Column("obfuscation_checksum", sa.String(length=128), nullable=True))
|
||||
|
||||
# Create the new 'bw_cli_commands' table
|
||||
op.create_table(
|
||||
"bw_cli_commands",
|
||||
sa.Column("id", sa.Integer(), sa.Identity(start=1, increment=1), nullable=False),
|
||||
sa.Column("name", sa.String(length=64), nullable=False),
|
||||
sa.Column("plugin_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("file_name", sa.String(length=256), nullable=False),
|
||||
sa.ForeignKeyConstraint(["plugin_id"], ["bw_plugins.id"], onupdate="cascade", ondelete="cascade"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("plugin_id", "name"),
|
||||
)
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.7' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop the 'bw_cli_commands' table
|
||||
op.drop_table("bw_cli_commands")
|
||||
|
||||
# Remove new columns from 'bw_plugin_pages'
|
||||
with op.batch_alter_table("bw_plugin_pages") as batch_op:
|
||||
batch_op.drop_column("obfuscation_checksum")
|
||||
batch_op.drop_column("obfuscation_file")
|
||||
|
||||
# Drop 'order' column from 'bw_settings'
|
||||
with op.batch_alter_table("bw_settings") as batch_op:
|
||||
batch_op.drop_column("order")
|
||||
|
||||
# Revert version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.6' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
"""Upgrade to version 1.5.11
|
||||
|
||||
Revision ID: c272a8c3979c
|
||||
Revises: 760f95e8bee7
|
||||
Create Date: 2024-12-17 08:41:42.203308
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "c272a8c3979c"
|
||||
down_revision: Union[str, None] = "760f95e8bee7"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Add 'non_draft_services' column as nullable with server default
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.add_column(sa.Column("non_draft_services", sa.Integer(), nullable=True, server_default="0"))
|
||||
|
||||
# Update existing rows
|
||||
op.execute("UPDATE bw_metadata SET non_draft_services = 0")
|
||||
|
||||
# Make the column NOT NULL and drop server_default
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.alter_column("non_draft_services", nullable=False, server_default=None)
|
||||
|
||||
# Update the version in metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.11' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop the 'non_draft_services' column
|
||||
with op.batch_alter_table("bw_metadata") as batch_op:
|
||||
batch_op.drop_column("non_draft_services")
|
||||
|
||||
# Revert version to 1.5.10
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.10' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
"""Upgrade to version 1.5.5
|
||||
|
||||
Revision ID: c9586782cd77
|
||||
Revises: 17a6fddfddc2
|
||||
Create Date: 2024-12-17 08:38:08.728703
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "c9586782cd77"
|
||||
down_revision: Union[str, None] = "17a6fddfddc2"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
METHODS_ENUM = sa.Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add new columns to bw_ui_users
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.add_column(sa.Column("is_two_factor_enabled", sa.Boolean(), nullable=False, server_default="0"))
|
||||
batch_op.add_column(sa.Column("secret_token", sa.String(length=32), nullable=True))
|
||||
batch_op.add_column(sa.Column("method", METHODS_ENUM, nullable=False, server_default="manual"))
|
||||
|
||||
# Create unique constraint on bw_ui_users
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.create_unique_constraint("uq_bw_ui_users_secret_token", ["secret_token"])
|
||||
|
||||
# Alter column lengths in bw_global_values and bw_services_settings
|
||||
with op.batch_alter_table("bw_global_values") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.String(8192), existing_type=sa.String(4096))
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.String(8192), existing_type=sa.String(4096))
|
||||
|
||||
# Update all new columns in a single statement
|
||||
op.execute("UPDATE bw_ui_users SET is_two_factor_enabled = false, method = 'manual'")
|
||||
|
||||
# Update version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.5' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert bw_ui_users changes
|
||||
with op.batch_alter_table("bw_ui_users") as batch_op:
|
||||
batch_op.drop_constraint("uq_bw_ui_users_secret_token", type_="unique")
|
||||
batch_op.drop_column("method")
|
||||
batch_op.drop_column("secret_token")
|
||||
batch_op.drop_column("is_two_factor_enabled")
|
||||
|
||||
# Revert column lengths in bw_global_values and bw_services_settings
|
||||
with op.batch_alter_table("bw_global_values") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.String(4096), existing_type=sa.String(8192))
|
||||
|
||||
with op.batch_alter_table("bw_services_settings") as batch_op:
|
||||
batch_op.alter_column("value", type_=sa.String(4096), existing_type=sa.String(8192))
|
||||
|
||||
# Revert version in bw_metadata
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.4' WHERE id = 1")
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"""Upgrade to version 1.5.3
|
||||
|
||||
Revision ID: eb3ca0f3f20c
|
||||
Revises: 259e352699f1
|
||||
Create Date: 2024-12-17 08:38:04.878280
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "eb3ca0f3f20c"
|
||||
down_revision: Union[str, None] = "259e352699f1"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Data migration: Update the version to 1.5.3
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.3' WHERE id = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Revert the version back to 1.5.2
|
||||
op.execute("UPDATE bw_metadata SET version = '1.5.2' WHERE id = 1")
|
||||
|
|
@ -306,7 +306,7 @@ class Metadata(Base):
|
|||
last_instances_change = Column(DateTime(timezone=True), nullable=True)
|
||||
failover = Column(Boolean, default=None, nullable=True)
|
||||
integration = Column(INTEGRATIONS_ENUM, default="Unknown", nullable=False)
|
||||
version = Column(String(32), default="1.6.0-beta", nullable=False)
|
||||
version = Column(String(32), default="1.6.0-rc1", nullable=False)
|
||||
|
||||
|
||||
## UI Models
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
alembic==1.14.0
|
||||
cryptography==44.0.0
|
||||
psycopg[binary,pool]==3.2.3
|
||||
PyMySQL==1.1.1
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
alembic==1.14.0 \
|
||||
--hash=sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25 \
|
||||
--hash=sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b
|
||||
# via -r requirements.in
|
||||
cffi==1.17.1 \
|
||||
--hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
|
||||
--hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
|
||||
|
|
@ -179,6 +183,73 @@ greenlet==3.1.1 \
|
|||
--hash=sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79 \
|
||||
--hash=sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f
|
||||
# via sqlalchemy
|
||||
mako==1.3.8 \
|
||||
--hash=sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627 \
|
||||
--hash=sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8
|
||||
# via alembic
|
||||
markupsafe==3.0.2 \
|
||||
--hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \
|
||||
--hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \
|
||||
--hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \
|
||||
--hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \
|
||||
--hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \
|
||||
--hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \
|
||||
--hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \
|
||||
--hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \
|
||||
--hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \
|
||||
--hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \
|
||||
--hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \
|
||||
--hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \
|
||||
--hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \
|
||||
--hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \
|
||||
--hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \
|
||||
--hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \
|
||||
--hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \
|
||||
--hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \
|
||||
--hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \
|
||||
--hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \
|
||||
--hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \
|
||||
--hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \
|
||||
--hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \
|
||||
--hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \
|
||||
--hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \
|
||||
--hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \
|
||||
--hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \
|
||||
--hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \
|
||||
--hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \
|
||||
--hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \
|
||||
--hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \
|
||||
--hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \
|
||||
--hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \
|
||||
--hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \
|
||||
--hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \
|
||||
--hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \
|
||||
--hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \
|
||||
--hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \
|
||||
--hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \
|
||||
--hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \
|
||||
--hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \
|
||||
--hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \
|
||||
--hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \
|
||||
--hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \
|
||||
--hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \
|
||||
--hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \
|
||||
--hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \
|
||||
--hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \
|
||||
--hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \
|
||||
--hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \
|
||||
--hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \
|
||||
--hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \
|
||||
--hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \
|
||||
--hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \
|
||||
--hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \
|
||||
--hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \
|
||||
--hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \
|
||||
--hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \
|
||||
--hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \
|
||||
--hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \
|
||||
--hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50
|
||||
# via mako
|
||||
psycopg==3.2.3 \
|
||||
--hash=sha256:644d3973fe26908c73d4be746074f6e5224b03c1101d302d9a53bf565ad64907 \
|
||||
--hash=sha256:a5764f67c27bec8bfac85764d23c534af2c27b893550377e37ce59c12aac47a2
|
||||
|
|
@ -319,11 +390,14 @@ sqlalchemy==2.0.36 \
|
|||
--hash=sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c \
|
||||
--hash=sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e \
|
||||
--hash=sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53
|
||||
# via -r requirements.in
|
||||
# via
|
||||
# -r requirements.in
|
||||
# alembic
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
# via
|
||||
# alembic
|
||||
# psycopg
|
||||
# psycopg-pool
|
||||
# sqlalchemy
|
||||
|
|
|
|||
|
|
@ -120,15 +120,12 @@ if __name__ == "__main__":
|
|||
|
||||
db = Database(LOGGER, sqlalchemy_string=dotenv_env.get("DATABASE_URI", getenv("DATABASE_URI", None)))
|
||||
|
||||
bunkerweb_version = get_version()
|
||||
db_metadata = db.get_metadata()
|
||||
db_initialized = not isinstance(db_metadata, str) and db_metadata["is_initialized"]
|
||||
|
||||
if not db_initialized:
|
||||
LOGGER.info("Database not initialized, initializing ...")
|
||||
ret, err = db.init_tables(
|
||||
[config.get_settings(), config.get_plugins("core"), config.get_plugins("external"), config.get_plugins("pro")], bunkerweb_version
|
||||
)
|
||||
ret, err = db.init_tables([config.get_settings(), config.get_plugins("core"), config.get_plugins("external"), config.get_plugins("pro")])
|
||||
|
||||
# Initialize database tables
|
||||
if err:
|
||||
|
|
@ -141,9 +138,7 @@ if __name__ == "__main__":
|
|||
else:
|
||||
LOGGER.info("Database is already initialized, checking for changes ...")
|
||||
|
||||
ret, err = db.init_tables(
|
||||
[config.get_settings(), config.get_plugins("core"), config.get_plugins("external"), config.get_plugins("pro")], bunkerweb_version
|
||||
)
|
||||
ret, err = db.init_tables([config.get_settings(), config.get_plugins("core"), config.get_plugins("external"), config.get_plugins("pro")])
|
||||
|
||||
if not ret and err:
|
||||
LOGGER.error(f"Exception while checking database tables : {err}")
|
||||
|
|
@ -153,7 +148,7 @@ if __name__ == "__main__":
|
|||
else:
|
||||
LOGGER.info("Database tables successfully updated")
|
||||
|
||||
err = db.initialize_db(version=bunkerweb_version, integration=integration)
|
||||
err = db.initialize_db(version=get_version(), integration=integration)
|
||||
|
||||
if err:
|
||||
LOGGER.error(f"Can't {'initialize' if not db_initialized else 'update'} database metadata : {err}")
|
||||
|
|
|
|||
|
|
@ -20,3 +20,11 @@ if [ -d /etc/nginx ]; then
|
|||
echo "ℹ️ Copy /etc/nginx to $output_path"
|
||||
do_and_check_cmd cp -R /etc/nginx "$output_path"
|
||||
fi
|
||||
|
||||
# If version is older that 1.5.12 included then create another file
|
||||
if [ -f /usr/share/bunkerweb/VERSION ]; then
|
||||
version=$(cat /usr/share/bunkerweb/VERSION)
|
||||
if dpkg --compare-versions "$version" le "1.5.12"; then
|
||||
do_and_check_cmd touch /var/tmp/bunkerweb_enable_scheduler
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
source /usr/share/bunkerweb/helpers/utils.sh
|
||||
|
||||
# Set the PYTHONPATH
|
||||
export PYTHONPATH=/usr/share/bunkerweb/deps/python
|
||||
export PYTHONPATH=/usr/share/bunkerweb/deps/python:/usr/share/bunkerweb/db
|
||||
|
||||
# Create the scheduler.env file if it doesn't exist
|
||||
if [ ! -f /etc/bunkerweb/scheduler.env ]; then
|
||||
|
|
@ -48,6 +48,101 @@ function start() {
|
|||
fi
|
||||
export SCHEDULER_LOG_TO_FILE
|
||||
|
||||
# Database migration section
|
||||
log "SYSTEMCTL" "ℹ️" "Checking database configuration..."
|
||||
cd /usr/share/bunkerweb/db/alembic || {
|
||||
log "SYSTEMCTL" "❌" "Failed to access database migration directory"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract and validate database type
|
||||
DATABASE_URI=${DATABASE_URI:-sqlite:////var/lib/bunkerweb/db.sqlite3}
|
||||
DATABASE=$(echo "$DATABASE_URI" | awk -F: '{print $1}' | awk -F+ '{print $1}')
|
||||
|
||||
# Validate database type with case-insensitive comparison
|
||||
db_type=$(echo "$DATABASE" | tr '[:upper:]' '[:lower:]')
|
||||
case "$db_type" in
|
||||
sqlite|mysql|mariadb|postgresql)
|
||||
log "SYSTEMCTL" "ℹ️" "Using database type: $DATABASE"
|
||||
;;
|
||||
*)
|
||||
log "SYSTEMCTL" "❌" "Unsupported database type: $DATABASE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Update configuration files
|
||||
if ! sed -i "s|^sqlalchemy\\.url =.*$|sqlalchemy.url = $DATABASE_URI|" alembic.ini; then
|
||||
log "SYSTEMCTL" "❌" "Failed to update database URL in configuration"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! sed -i "s|^version_locations =.*$|version_locations = ${DATABASE}_versions|" alembic.ini; then
|
||||
log "SYSTEMCTL" "❌" "Failed to update version locations in configuration"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check current version and stamp
|
||||
log "SYSTEMCTL" "ℹ️" "Checking database version..."
|
||||
installed_version=$(cat /usr/share/bunkerweb/VERSION)
|
||||
# Create temporary Python script
|
||||
cat > /tmp/version_check.py << EOL
|
||||
import sqlalchemy as sa
|
||||
from os import getenv
|
||||
|
||||
from Database import Database
|
||||
from logger import setup_logger
|
||||
|
||||
LOGGER = setup_logger('Scheduler', getenv('CUSTOM_LOG_LEVEL', getenv('LOG_LEVEL', 'INFO')))
|
||||
|
||||
engine = Database(LOGGER, '${DATABASE_URI}').sql_engine
|
||||
with engine.connect() as conn:
|
||||
try:
|
||||
result = conn.execute(sa.text('SELECT version FROM bw_metadata WHERE id = 1'))
|
||||
print(next(result)[0])
|
||||
except BaseException as e:
|
||||
if 'doesn\\'t exist' not in str(e):
|
||||
print('none')
|
||||
print('${installed_version}')
|
||||
EOL
|
||||
|
||||
current_version=$(sudo -E -u nginx -g nginx /bin/bash -c "PYTHONPATH=$PYTHONPATH python3 /tmp/version_check.py")
|
||||
rm -f /tmp/version_check.py
|
||||
|
||||
if [ "$current_version" == "none" ]; then
|
||||
log "SYSTEMCTL" "❌" "Failed to retrieve database version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$current_version" != "$installed_version" ]; then
|
||||
# Find the corresponding Alembic revision by scanning migration files
|
||||
MIGRATION_DIR="/usr/share/bunkerweb/db/alembic/${DATABASE}_versions"
|
||||
NORMALIZED_VERSION=$(echo "$current_version" | tr '.' '_' | tr '-' '_')
|
||||
REVISION=$(find "$MIGRATION_DIR" -maxdepth 1 -type f -name "*_upgrade_to_version_${NORMALIZED_VERSION}.py" -exec basename {} \; | awk -F_ '{print $1}')
|
||||
|
||||
if [ -z "$REVISION" ]; then
|
||||
log "SYSTEMCTL" "❌" "No migration file found for database version: $current_version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stamp the database with the determined revision
|
||||
if ! sudo -E -u nginx -g nginx /bin/bash -c "PYTHONPATH=$PYTHONPATH python3 -m alembic stamp \"$REVISION\""; then
|
||||
log "SYSTEMCTL" "❌" "Failed to stamp database with revision: $REVISION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run database migration
|
||||
log "SYSTEMCTL" "ℹ️" "Running database migration..."
|
||||
if ! sudo -E -u nginx -g nginx /bin/bash -c "PYTHONPATH=$PYTHONPATH python3 -m alembic upgrade head"; then
|
||||
log "SYSTEMCTL" "❌" "Database migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "SYSTEMCTL" "✅" "Database migration completed successfully"
|
||||
fi
|
||||
|
||||
cd - > /dev/null || exit 1
|
||||
|
||||
# Execute scheduler
|
||||
log "SYSTEMCTL" "ℹ️ " "Executing scheduler ..."
|
||||
sudo -E -u nginx -g nginx /bin/bash -c "PYTHONPATH=$PYTHONPATH /usr/share/bunkerweb/scheduler/main.py --variables /etc/bunkerweb/variables.env"
|
||||
|
|
|
|||
|
|
@ -116,19 +116,27 @@ fi
|
|||
|
||||
# Create scheduler if necessary
|
||||
if {
|
||||
[ "${MANAGER_MODE:-yes}" != "no" ] || [ "${WORKER_MODE:-no}" = "no" ];
|
||||
{
|
||||
[ -z "$MANAGER_MODE" ] && [ -z "$WORKER_MODE" ];
|
||||
} || {
|
||||
[ "${MANAGER_MODE:-yes}" != "no" ] || [ "${WORKER_MODE:-no}" = "no" ];
|
||||
};
|
||||
} && [ "$SERVICE_SCHEDULER" != "no" ]; then
|
||||
if [ -f /var/tmp/bunkerweb_upgrade ]; then
|
||||
# Reload the bunkerweb-scheduler service if running
|
||||
if systemctl is-active --quiet bunkerweb-scheduler; then
|
||||
echo "Restarting the bunkerweb-scheduler service..."
|
||||
do_and_check_cmd systemctl restart bunkerweb-scheduler
|
||||
fi
|
||||
else
|
||||
if [[ -f /var/tmp/bunkerweb_enable_scheduler || ! -f /var/tmp/bunkerweb_upgrade ]]; then
|
||||
# Auto start BW Scheduler service on boot and start it now
|
||||
echo "Enabling and starting the bunkerweb-scheduler service..."
|
||||
do_and_check_cmd systemctl enable bunkerweb-scheduler
|
||||
do_and_check_cmd systemctl start bunkerweb-scheduler
|
||||
|
||||
if [ -f /var/tmp/bunkerweb_enable_scheduler ]; then
|
||||
rm -f /var/tmp/bunkerweb_enable_scheduler
|
||||
fi
|
||||
else
|
||||
# Reload the bunkerweb-scheduler service if running
|
||||
if systemctl is-active --quiet bunkerweb-scheduler; then
|
||||
echo "Restarting the bunkerweb-scheduler service..."
|
||||
do_and_check_cmd systemctl restart bunkerweb-scheduler
|
||||
fi
|
||||
fi
|
||||
elif systemctl is-active --quiet bunkerweb-scheduler; then
|
||||
echo "Disabling the bunkerweb-scheduler service..."
|
||||
|
|
@ -138,7 +146,11 @@ fi
|
|||
|
||||
# Create web UI if necessary
|
||||
if {
|
||||
[ "${MANAGER_MODE:-yes}" != "no" ] || [ "${WORKER_MODE:-no}" = "no" ];
|
||||
{
|
||||
[ -z "$MANAGER_MODE" ] && [ -z "$WORKER_MODE" ];
|
||||
} || {
|
||||
[ "${MANAGER_MODE:-yes}" != "no" ] || [ "${WORKER_MODE:-no}" = "no" ];
|
||||
};
|
||||
} && [ "$SERVICE_UI" != "no" ]; then
|
||||
if [ -f /var/tmp/bunkerweb_upgrade ]; then
|
||||
# Reload the bunkerweb-ui service if running
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ FROM python:3.13-alpine@sha256:fcbcbbecdeae71d3b77445d9144d1914df55110f825ab62b0
|
|||
RUN umask 027
|
||||
|
||||
# Install runtime dependencies and add scheduler user
|
||||
RUN apk add --no-cache bash unzip libgcc libstdc++ libpq openssl libmagic mariadb-connector-c mariadb-client postgresql-client sqlite tzdata && \
|
||||
RUN apk add --no-cache bash unzip libgcc libstdc++ libpq openssl libmagic mariadb-connector-c mariadb-client postgresql-client sqlite tzdata sed grep && \
|
||||
addgroup -g 101 scheduler && \
|
||||
adduser -h /var/cache/nginx -g scheduler -s /bin/sh -G scheduler -D -H -u 101 scheduler
|
||||
|
||||
|
|
@ -69,6 +69,8 @@ RUN cp helpers/bwcli /usr/bin/ && \
|
|||
find core/ -type f -name "*.sh" ! -path "core/modsecurity/files/*" -print0 | xargs -0 chmod 750 && \
|
||||
find core/ -type f -name "*.py" ! -path "core/modsecurity/files/*" -print0 | xargs -0 chmod 750 && \
|
||||
chmod 750 cli/main.py gen/*.py scheduler/main.py scheduler/entrypoint.sh helpers/*.sh deps/python/bin/* /usr/bin/bwcli && \
|
||||
chmod 770 db/alembic/alembic.ini db/alembic/env.py && \
|
||||
chmod u+w db/alembic && \
|
||||
chmod 660 INTEGRATION
|
||||
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ COPY --chown=root:scheduler --chmod=770 src/bw/misc/asn.mmdb /var/tmp/bunkerweb/
|
|||
COPY --chown=root:scheduler --chmod=770 src/bw/misc/country.mmdb /var/tmp/bunkerweb/country.mmdb
|
||||
|
||||
LABEL maintainer="Bunkerity <contact@bunkerity.com>"
|
||||
LABEL version="1.6.0-beta"
|
||||
LABEL version="1.6.0-rc1"
|
||||
LABEL url="https://www.bunkerweb.io"
|
||||
LABEL bunkerweb.type="scheduler"
|
||||
|
||||
|
|
@ -88,4 +90,6 @@ USER scheduler:scheduler
|
|||
|
||||
HEALTHCHECK --interval=10s --timeout=10s --start-period=30s --retries=6 CMD /usr/share/bunkerweb/helpers/healthcheck-scheduler.sh
|
||||
|
||||
ENV PYTHONPATH=/usr/share/bunkerweb/deps/python:/usr/share/bunkerweb/db
|
||||
|
||||
ENTRYPOINT [ "./entrypoint.sh" ]
|
||||
|
|
|
|||
|
|
@ -34,6 +34,97 @@ elif [[ $(echo "$AUTOCONF_MODE" | awk '{print tolower($0)}') == "yes" ]] ; then
|
|||
echo "Autoconf" > /usr/share/bunkerweb/INTEGRATION
|
||||
fi
|
||||
|
||||
# Database migration section
|
||||
log "ENTRYPOINT" "ℹ️" "Checking database configuration..."
|
||||
cd /usr/share/bunkerweb/db/alembic || {
|
||||
log "ENTRYPOINT" "❌" "Failed to access database migration directory"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract and validate database type
|
||||
DATABASE_URI=${DATABASE_URI:-sqlite:////var/lib/bunkerweb/db.sqlite3}
|
||||
DATABASE=$(echo "$DATABASE_URI" | awk -F: '{print $1}' | awk -F+ '{print $1}')
|
||||
|
||||
# Validate database type with case-insensitive comparison
|
||||
db_type=$(echo "$DATABASE" | tr '[:upper:]' '[:lower:]')
|
||||
case "$db_type" in
|
||||
sqlite|mysql|mariadb|postgresql)
|
||||
log "ENTRYPOINT" "ℹ️" "Using database type: $DATABASE"
|
||||
;;
|
||||
*)
|
||||
log "ENTRYPOINT" "❌" "Unsupported database type: $DATABASE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Update configuration files
|
||||
if ! sed -i "s|^sqlalchemy\\.url =.*$|sqlalchemy.url = $DATABASE_URI|" alembic.ini; then
|
||||
log "ENTRYPOINT" "❌" "Failed to update database URL in configuration"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! sed -i "s|^version_locations =.*$|version_locations = ${DATABASE}_versions|" alembic.ini; then
|
||||
log "ENTRYPOINT" "❌" "Failed to update version locations in configuration"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check current version and stamp
|
||||
log "ENTRYPOINT" "ℹ️" "Checking database version..."
|
||||
installed_version=$(cat /usr/share/bunkerweb/VERSION)
|
||||
current_version=$(python3 -c "
|
||||
import sqlalchemy as sa
|
||||
from os import getenv
|
||||
|
||||
from Database import Database
|
||||
from logger import setup_logger
|
||||
|
||||
LOGGER = setup_logger('Scheduler', getenv('CUSTOM_LOG_LEVEL', getenv('LOG_LEVEL', 'INFO')))
|
||||
|
||||
engine = Database(LOGGER, '${DATABASE_URI}').sql_engine
|
||||
with engine.connect() as conn:
|
||||
try:
|
||||
result = conn.execute(sa.text('SELECT version FROM bw_metadata WHERE id = 1'))
|
||||
print(next(result)[0])
|
||||
except BaseException as e:
|
||||
if 'doesn\'t exist' not in str(e):
|
||||
print('none')
|
||||
print('${installed_version}')
|
||||
")
|
||||
|
||||
if [ "$current_version" == "none" ]; then
|
||||
log "ENTRYPOINT" "❌" "Failed to retrieve database version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$current_version" != "$installed_version" ]; then
|
||||
# Find the corresponding Alembic revision by scanning migration files
|
||||
MIGRATION_DIR="/usr/share/bunkerweb/db/alembic/${DATABASE}_versions"
|
||||
NORMALIZED_VERSION=$(echo "$current_version" | tr '.' '_' | tr '-' '_')
|
||||
REVISION=$(find "$MIGRATION_DIR" -maxdepth 1 -type f -name "*_upgrade_to_version_${NORMALIZED_VERSION}.py" -exec basename {} \; | awk -F_ '{print $1}')
|
||||
|
||||
if [ -z "$REVISION" ]; then
|
||||
log "ENTRYPOINT" "❌" "No migration file found for database version: $current_version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stamp the database with the determined revision
|
||||
if ! python3 -m alembic stamp "$REVISION"; then
|
||||
log "ENTRYPOINT" "❌" "Failed to stamp database with revision: $REVISION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run database migration
|
||||
log "ENTRYPOINT" "ℹ️" "Running database migration..."
|
||||
if ! python3 -m alembic upgrade head; then
|
||||
log "ENTRYPOINT" "❌" "Database migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "ENTRYPOINT" "✅" "Database migration completed successfully"
|
||||
fi
|
||||
|
||||
cd - > /dev/null || exit 1
|
||||
|
||||
# execute jobs
|
||||
log "ENTRYPOINT" "ℹ️ " "Executing scheduler ..."
|
||||
/usr/share/bunkerweb/scheduler/main.py &
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
|
|||
from dotenv import dotenv_values
|
||||
from schedule import every as schedule_every, run_pending
|
||||
|
||||
from common_utils import bytes_hash, dict_to_frozenset, get_version # type: ignore
|
||||
from common_utils import bytes_hash, dict_to_frozenset # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
from Database import Database # type: ignore
|
||||
from JobScheduler import JobScheduler
|
||||
|
|
@ -396,7 +396,13 @@ def healthcheck_job():
|
|||
for db_instance in SCHEDULER.db.get_instances():
|
||||
bw_instance = API(f"http://{db_instance['hostname']}:{db_instance['port']}", db_instance["server_name"])
|
||||
try:
|
||||
sent, err, status, resp = bw_instance.request("GET", "health")
|
||||
try:
|
||||
sent, err, status, resp = bw_instance.request("GET", "health")
|
||||
except BaseException as e:
|
||||
err = str(e)
|
||||
sent = False
|
||||
status = 500
|
||||
resp = {"status": "down", "msg": err}
|
||||
|
||||
success = True
|
||||
if not sent:
|
||||
|
|
@ -493,27 +499,6 @@ if __name__ == "__main__":
|
|||
|
||||
APPLYING_CHANGES.set()
|
||||
|
||||
db_version = SCHEDULER.db.get_version()
|
||||
if not db_version.startswith("Error") and db_version != get_version():
|
||||
LOGGER.warning("BunkerWeb version changed, creating a backup of the database and proceeding with the upgrade ...")
|
||||
SCHEDULER.env = {
|
||||
"DATABASE_URI": SCHEDULER.db.database_uri,
|
||||
"USE_BACKUP": "yes",
|
||||
"FORCE_BACKUP": "yes",
|
||||
"BACKUP_SCHEDULE": "daily",
|
||||
"BACKUP_ROTATION": "7",
|
||||
"BACKUP_DIRECTORY": "/var/lib/bunkerweb/upgrade_backups",
|
||||
"LOG_LEVEL": getenv("CUSTOM_LOG_LEVEL", getenv("LOG_LEVEL", "notice")),
|
||||
"RELOAD_MIN_TIMEOUT": str(RELOAD_MIN_TIMEOUT),
|
||||
}
|
||||
|
||||
if not SCHEDULER.run_single("backup-data"):
|
||||
LOGGER.error("backup-data job failed, stopping ...")
|
||||
stop(1)
|
||||
LOGGER.info("Backup completed successfully, if you want to restore the backup, you can find it in /var/lib/bunkerweb/upgrade_backups")
|
||||
|
||||
rmtree(join(sep, "etc", "bunkerweb", "pro", "plugins", "letsencrypt_dns"), ignore_errors=True)
|
||||
|
||||
if SCHEDULER.db.readonly:
|
||||
LOGGER.warning("The database is read-only, no need to save the changes in the configuration as they will not be saved")
|
||||
else:
|
||||
|
|
@ -623,6 +608,9 @@ if __name__ == "__main__":
|
|||
with file.open("r", encoding="utf-8") as f:
|
||||
plugin_data = json_load(f)
|
||||
|
||||
if plugin_data["id"] == "letsencrypt_dns":
|
||||
continue
|
||||
|
||||
checksum = bytes_hash(plugin_content, algorithm="sha256")
|
||||
common_data = plugin_data | {
|
||||
"type": _type,
|
||||
|
|
|
|||
Loading…
Reference in a new issue