init CTFd source
Some checks are pending
Linting / Linting (3.11) (push) Waiting to run
Mirror core-theme / mirror (push) Waiting to run

This commit is contained in:
gkr
2025-12-25 09:39:21 +08:00
commit 2e06f92c64
1047 changed files with 150349 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
"""Add table for comments
Revision ID: 0366ba6575ca
Revises: 1093835a1051
Create Date: 2020-08-14 00:46:54.161120
"""
from alembic import op # noqa: I001
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "0366ba6575ca"
down_revision = "1093835a1051"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"comments",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("content", sa.Text(), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.Column("author_id", sa.Integer(), nullable=True),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.Column("page_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["author_id"], ["users.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(
["challenge_id"], ["challenges.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["page_id"], ["pages.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("comments")
# ### end Alembic commands ###

View File

@@ -0,0 +1,28 @@
"""Add format to Pages
Revision ID: 07dfbe5e1edc
Revises: 75e8ab9a0014
Create Date: 2021-06-15 19:57:37.410152
"""
from alembic import op # noqa: I001
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "07dfbe5e1edc"
down_revision = "75e8ab9a0014"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("pages", sa.Column("format", sa.String(length=80), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("pages", "format")
# ### end Alembic commands ###

View File

@@ -0,0 +1,34 @@
"""Add Tokens table to store user access tokens
Revision ID: 080d29b15cd3
Revises: b295b033364d
Create Date: 2019-11-03 18:21:04.827015
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "080d29b15cd3"
down_revision = "b295b033364d"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"tokens",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("type", sa.String(length=32), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("created", sa.DateTime(), nullable=True),
sa.Column("expiration", sa.DateTime(), nullable=True),
sa.Column("value", sa.String(length=128), nullable=True),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("value"),
)
def downgrade():
op.drop_table("tokens")

View File

@@ -0,0 +1,23 @@
"""Add language column to Users table
Revision ID: 0def790057c1
Revises: 46a278193a94
Create Date: 2023-04-19 00:56:54.592584
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "0def790057c1"
down_revision = "46a278193a94"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("users", sa.Column("language", sa.String(length=32), nullable=True))
def downgrade():
op.drop_column("users", "language")

View File

@@ -0,0 +1,70 @@
"""Add default email templates
Revision ID: 1093835a1051
Revises: a03403986a32
Create Date: 2020-02-15 01:32:10.959373
"""
from alembic import op
from sqlalchemy.sql import column, table
from CTFd.models import db
from CTFd.utils.email import (
DEFAULT_PASSWORD_RESET_BODY,
DEFAULT_PASSWORD_RESET_SUBJECT,
DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_BODY,
DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_SUBJECT,
DEFAULT_USER_CREATION_EMAIL_BODY,
DEFAULT_USER_CREATION_EMAIL_SUBJECT,
DEFAULT_VERIFICATION_EMAIL_BODY,
DEFAULT_VERIFICATION_EMAIL_SUBJECT,
)
# revision identifiers, used by Alembic.
revision = "1093835a1051"
down_revision = "a03403986a32"
branch_labels = None
depends_on = None
configs_table = table(
"config", column("id", db.Integer), column("key", db.Text), column("value", db.Text)
)
def get_config(key):
connection = op.get_bind()
return connection.execute(
configs_table.select().where(configs_table.c.key == key).limit(1)
).fetchone()
def set_config(key, value):
connection = op.get_bind()
connection.execute(configs_table.insert().values(key=key, value=value))
def upgrade():
# Only run if this instance already been setup before
if bool(get_config("setup")) is True:
for k, v in [
("password_reset_body", DEFAULT_PASSWORD_RESET_BODY),
("password_reset_subject", DEFAULT_PASSWORD_RESET_SUBJECT),
(
"successful_registration_email_body",
DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_BODY,
),
(
"successful_registration_email_subject",
DEFAULT_SUCCESSFUL_REGISTRATION_EMAIL_SUBJECT,
),
("user_creation_email_body", DEFAULT_USER_CREATION_EMAIL_BODY),
("user_creation_email_subject", DEFAULT_USER_CREATION_EMAIL_SUBJECT),
("verification_email_body", DEFAULT_VERIFICATION_EMAIL_BODY),
("verification_email_subject", DEFAULT_VERIFICATION_EMAIL_SUBJECT),
]:
if get_config(k) is None:
set_config(k, v)
def downgrade():
pass

View File

@@ -0,0 +1,45 @@
"""Convert rating values to votes
Revision ID: 24ad6790bc3c
Revises: 55623b100da8
Create Date: 2025-09-03 05:19:54.151054
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "24ad6790bc3c"
down_revision = "55623b100da8"
branch_labels = None
depends_on = None
def upgrade():
# Convert 5-star ratings to upvote/downvote system
# Values 1-2 become -1 (downvote)
# Values 3-5 become 1 (upvote)
connection = op.get_bind()
# Update ratings with values 1 or 2 to -1 (downvote)
connection.execute(sa.text("UPDATE ratings SET value = -1 WHERE value IN (1, 2)"))
# Update ratings with values 3, 4, or 5 to 1 (upvote)
connection.execute(sa.text("UPDATE ratings SET value = 1 WHERE value IN (3, 4, 5)"))
def downgrade():
# This downgrade is not reversible since we lose the original 5-star granularity
# We can only convert back to a simplified 5-star system
# -1 (downvote) becomes 1 (1 star)
# 1 (upvote) becomes 5 (5 stars)
connection = op.get_bind()
# Convert downvotes (-1) to 1-star ratings
connection.execute(sa.text("UPDATE ratings SET value = 1 WHERE value = -1"))
# Convert upvotes (1) to 5-star ratings
connection.execute(sa.text("UPDATE ratings SET value = 5 WHERE value = 1"))

View File

@@ -0,0 +1,42 @@
"""Add ratings table
Revision ID: 364b4efa1686
Revises: 662d728ad7da
Create Date: 2025-08-17 07:07:41.484323
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "364b4efa1686"
down_revision = "662d728ad7da"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"ratings",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("value", sa.Integer(), nullable=True),
sa.Column("review", sa.String(length=2000), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["challenge_id"], ["challenges.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("user_id", "challenge_id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("ratings")
# ### end Alembic commands ###

View File

@@ -0,0 +1,46 @@
"""Enable millisecond precision in MySQL datetime
Revision ID: 46a278193a94
Revises: 4d3c1b59d011
Create Date: 2022-11-01 23:27:44.620893
"""
from alembic import op # noqa: I001
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = "46a278193a94"
down_revision = "4d3c1b59d011"
branch_labels = None
depends_on = None
def upgrade():
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith("mysql"):
get_columns = "SELECT `TABLE_NAME`, `COLUMN_NAME` FROM `information_schema`.`COLUMNS` WHERE `table_schema`=DATABASE() AND `DATA_TYPE`='datetime' AND `COLUMN_TYPE`='datetime';"
conn = op.get_bind()
columns = conn.execute(get_columns).fetchall()
for table_name, column_name in columns:
op.alter_column(
table_name=table_name,
column_name=column_name,
type_=mysql.DATETIME(fsp=6),
)
def downgrade():
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith("mysql"):
get_columns = "SELECT `TABLE_NAME`, `COLUMN_NAME` FROM `information_schema`.`COLUMNS` WHERE `table_schema`=DATABASE() AND `DATA_TYPE`='datetime' AND `COLUMN_TYPE`='datetime(6)';"
conn = op.get_bind()
columns = conn.execute(get_columns).fetchall()
for table_name, column_name in columns:
op.alter_column(
table_name=table_name,
column_name=column_name,
type_=mysql.DATETIME(fsp=0),
)

View File

@@ -0,0 +1,32 @@
"""Add next_id to Challenges table
Revision ID: 4d3c1b59d011
Revises: 6012fe8de495
Create Date: 2022-04-07 03:53:27.554190
"""
from alembic import op # noqa: I001
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "4d3c1b59d011"
down_revision = "6012fe8de495"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("challenges", sa.Column("next_id", sa.Integer(), nullable=True))
op.create_foreign_key(
None, "challenges", "challenges", ["next_id"], ["id"], ondelete="SET NULL"
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "challenges", type_="foreignkey")
op.drop_column("challenges", "next_id")
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""Add type to awards
Revision ID: 4e4d5a9ea000
Revises: 8369118943a1
Create Date: 2019-04-07 19:37:17.872128
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "4e4d5a9ea000"
down_revision = "8369118943a1"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"awards",
sa.Column(
"type", sa.String(length=80), nullable=True, server_default="standard"
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("awards", "type")
# ### end Alembic commands ###

View File

@@ -0,0 +1,24 @@
"""Add attribution to Challenges
Revision ID: 4fe3eeed9a9d
Revises: a02c5bf43407
Create Date: 2024-09-07 01:02:28.997761
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "4fe3eeed9a9d"
down_revision = "a02c5bf43407"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("challenges", sa.Column("attribution", sa.Text(), nullable=True))
def downgrade():
op.drop_column("challenges", "attribution")

View File

@@ -0,0 +1,28 @@
"""Add target column to Tracking
Revision ID: 55623b100da8
Revises: 5c98d9253f56
Create Date: 2025-08-26 23:30:29.170546
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "55623b100da8"
down_revision = "5c98d9253f56"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("tracking", sa.Column("target", sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("tracking", "target")
# ### end Alembic commands ###

View File

@@ -0,0 +1,23 @@
"""Add sha1sum field to Files
Revision ID: 5c4996aeb2cb
Revises: 9e6f6578ca84
Create Date: 2024-01-07 13:09:08.843903
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "5c4996aeb2cb"
down_revision = "9e6f6578ca84"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("files", sa.Column("sha1sum", sa.String(length=40), nullable=True))
def downgrade():
op.drop_column("files", "sha1sum")

View File

@@ -0,0 +1,119 @@
"""Rename core-beta to core
Revision ID: 5c98d9253f56
Revises: 364b4efa1686
Create Date: 2025-08-24 16:23:18.795064
"""
from alembic import op
from sqlalchemy.sql import column, table
from CTFd.models import db
# revision identifiers, used by Alembic.
revision = "5c98d9253f56"
down_revision = "364b4efa1686"
branch_labels = None
depends_on = None
configs_table = table(
"config", column("id", db.Integer), column("key", db.Text), column("value", db.Text)
)
pages_table = table("pages", column("id", db.Integer), column("content", db.Text))
def upgrade():
connection = op.get_bind()
# Define theme transformations: old_theme -> new_theme
theme_transformations = {
"core-beta": "core",
"hacker-beta-theme": "hacker-theme",
"learning-beta-theme": "learning-theme",
"learning": "learning-theme",
}
# Find the ctf_theme config entry
theme_config = connection.execute(
configs_table.select().where(configs_table.c.key == "ctf_theme")
).fetchone()
# Update ctf_theme config
if (
theme_config
and theme_config.value
and theme_config.value in theme_transformations
):
new_value = theme_transformations[theme_config.value]
connection.execute(
configs_table.update()
.where(configs_table.c.id == theme_config.id)
.values(value=new_value)
)
# Update pages content for all theme transformations
for old_theme, new_theme in theme_transformations.items():
old_path = f"themes/{old_theme}/static"
new_path = f"themes/{new_theme}/static"
pages_with_old_theme = connection.execute(
pages_table.select().where(pages_table.c.content.like(f"%{old_path}%"))
).fetchall()
for page in pages_with_old_theme:
if page.content:
new_content = page.content.replace(old_path, new_path)
connection.execute(
pages_table.update()
.where(pages_table.c.id == page.id)
.values(content=new_content)
)
def downgrade():
connection = op.get_bind()
# Define reverse theme transformations: new_theme -> old_theme
reverse_theme_transformations = {
"core": "core-beta",
"hacker-theme": "hacker-beta-theme",
"learning-theme": "learning-beta-theme",
}
# Find the ctf_theme config entry
theme_config = connection.execute(
configs_table.select().where(configs_table.c.key == "ctf_theme")
).fetchone()
# Restore ctf_theme config
if (
theme_config
and theme_config.value
and theme_config.value in reverse_theme_transformations
):
new_value = reverse_theme_transformations[theme_config.value]
connection.execute(
configs_table.update()
.where(configs_table.c.id == theme_config.id)
.values(value=new_value)
)
# Restore pages content for all theme transformations
for new_theme, old_theme in reverse_theme_transformations.items():
new_path = f"themes/{new_theme}/static"
old_path = f"themes/{old_theme}/static"
pages_with_new_theme = connection.execute(
pages_table.select().where(pages_table.c.content.like(f"%{new_path}%"))
).fetchall()
for page in pages_with_new_theme:
if page.content and old_path not in page.content:
new_content = page.content.replace(new_path, old_path)
connection.execute(
pages_table.update()
.where(pages_table.c.id == page.id)
.values(content=new_content)
)

View File

@@ -0,0 +1,28 @@
"""Add connection_info column to Challenges
Revision ID: 6012fe8de495
Revises: ef87d69ec29a
Create Date: 2021-07-30 03:50:54.219124
"""
from alembic import op # noqa: I001
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "6012fe8de495"
down_revision = "ef87d69ec29a"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("challenges", sa.Column("connection_info", sa.Text(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("challenges", "connection_info")
# ### end Alembic commands ###

View File

@@ -0,0 +1,43 @@
"""Add Solutions table
Revision ID: 62bf576b2cd3
Revises: a49ad66aa0f1
Create Date: 2025-07-28 20:04:45.082529
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "62bf576b2cd3"
down_revision = "a49ad66aa0f1"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"solutions",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("content", sa.Text(), nullable=True),
sa.Column("state", sa.String(length=80), nullable=False),
sa.ForeignKeyConstraint(
["challenge_id"], ["challenges.id"], ondelete="CASCADE"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("challenge_id"),
)
op.add_column("files", sa.Column("solution_id", sa.Integer(), nullable=True))
op.create_foreign_key(None, "files", "solutions", ["solution_id"], ["id"])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "files", type_="foreignkey")
op.drop_column("files", "solution_id")
op.drop_table("solutions")
# ### end Alembic commands ###

View File

@@ -0,0 +1,28 @@
"""Add change_password to Users
Revision ID: 662d728ad7da
Revises: f73a96c97449
Create Date: 2025-08-11 16:38:48.891702
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "662d728ad7da"
down_revision = "f73a96c97449"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("users", sa.Column("change_password", sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("users", "change_password")
# ### end Alembic commands ###

View File

@@ -0,0 +1,36 @@
"""Add dynamic scoring columns to Challenges table
Revision ID: 67ebab6de598
Revises: 24ad6790bc3c
Create Date: 2025-09-11 08:57:33.156731
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "67ebab6de598"
down_revision = "24ad6790bc3c"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("challenges", sa.Column("initial", sa.Integer(), nullable=True))
op.add_column("challenges", sa.Column("minimum", sa.Integer(), nullable=True))
op.add_column("challenges", sa.Column("decay", sa.Integer(), nullable=True))
op.add_column(
"challenges", sa.Column("function", sa.String(length=32), nullable=True)
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("challenges", "function")
op.drop_column("challenges", "decay")
op.drop_column("challenges", "minimum")
op.drop_column("challenges", "initial")
# ### end Alembic commands ###

View File

@@ -0,0 +1,53 @@
"""Add Fields and FieldEntries tables
Revision ID: 75e8ab9a0014
Revises: 0366ba6575ca
Create Date: 2020-08-19 00:36:17.579497
"""
from alembic import op # noqa: I001
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "75e8ab9a0014"
down_revision = "0366ba6575ca"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"fields",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.Text(), nullable=True),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("field_type", sa.String(length=80), nullable=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("required", sa.Boolean(), nullable=True),
sa.Column("public", sa.Boolean(), nullable=True),
sa.Column("editable", sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"field_entries",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("value", sa.JSON(), nullable=True),
sa.Column("field_id", sa.Integer(), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["field_id"], ["fields.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("field_entries")
op.drop_table("fields")
# ### end Alembic commands ###

View File

@@ -0,0 +1,249 @@
"""Initial Revision
Revision ID: 8369118943a1
Revises:
Create Date: 2018-11-05 01:06:24.495010
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "8369118943a1"
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"challenges",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=80), nullable=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("max_attempts", sa.Integer(), nullable=True),
sa.Column("value", sa.Integer(), nullable=True),
sa.Column("category", sa.String(length=80), nullable=True),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("state", sa.String(length=80), nullable=False),
sa.Column("requirements", sa.JSON(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"config",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("key", sa.Text(), nullable=True),
sa.Column("value", sa.Text(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"pages",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("title", sa.String(length=80), nullable=True),
sa.Column("route", sa.String(length=128), nullable=True),
sa.Column("content", sa.Text(), nullable=True),
sa.Column("draft", sa.Boolean(), nullable=True),
sa.Column("hidden", sa.Boolean(), nullable=True),
sa.Column("auth_required", sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("route"),
)
op.create_table(
"teams",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("oauth_id", sa.Integer(), nullable=True),
sa.Column("name", sa.String(length=128), nullable=True),
sa.Column("email", sa.String(length=128), nullable=True),
sa.Column("password", sa.String(length=128), nullable=True),
sa.Column("secret", sa.String(length=128), nullable=True),
sa.Column("website", sa.String(length=128), nullable=True),
sa.Column("affiliation", sa.String(length=128), nullable=True),
sa.Column("country", sa.String(length=32), nullable=True),
sa.Column("bracket", sa.String(length=32), nullable=True),
sa.Column("hidden", sa.Boolean(), nullable=True),
sa.Column("banned", sa.Boolean(), nullable=True),
sa.Column("created", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("email"),
sa.UniqueConstraint("id", "oauth_id"),
sa.UniqueConstraint("oauth_id"),
)
op.create_table(
"dynamic_challenge",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("initial", sa.Integer(), nullable=True),
sa.Column("minimum", sa.Integer(), nullable=True),
sa.Column("decay", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["id"], ["challenges.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"files",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("location", sa.Text(), nullable=True),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("page_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["challenge_id"], ["challenges.id"]),
sa.ForeignKeyConstraint(["page_id"], ["pages.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"flags",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("content", sa.Text(), nullable=True),
sa.Column("data", sa.Text(), nullable=True),
sa.ForeignKeyConstraint(["challenge_id"], ["challenges.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"hints",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("content", sa.Text(), nullable=True),
sa.Column("cost", sa.Integer(), nullable=True),
sa.Column("requirements", sa.JSON(), nullable=True),
sa.ForeignKeyConstraint(["challenge_id"], ["challenges.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"tags",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("value", sa.String(length=80), nullable=True),
sa.ForeignKeyConstraint(["challenge_id"], ["challenges.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"users",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("oauth_id", sa.Integer(), nullable=True),
sa.Column("name", sa.String(length=128), nullable=True),
sa.Column("password", sa.String(length=128), nullable=True),
sa.Column("email", sa.String(length=128), nullable=True),
sa.Column("type", sa.String(length=80), nullable=True),
sa.Column("secret", sa.String(length=128), nullable=True),
sa.Column("website", sa.String(length=128), nullable=True),
sa.Column("affiliation", sa.String(length=128), nullable=True),
sa.Column("country", sa.String(length=32), nullable=True),
sa.Column("bracket", sa.String(length=32), nullable=True),
sa.Column("hidden", sa.Boolean(), nullable=True),
sa.Column("banned", sa.Boolean(), nullable=True),
sa.Column("verified", sa.Boolean(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.Column("created", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"]),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("email"),
sa.UniqueConstraint("id", "oauth_id"),
sa.UniqueConstraint("oauth_id"),
)
op.create_table(
"awards",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.Column("name", sa.String(length=80), nullable=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.Column("value", sa.Integer(), nullable=True),
sa.Column("category", sa.String(length=80), nullable=True),
sa.Column("icon", sa.Text(), nullable=True),
sa.Column("requirements", sa.JSON(), nullable=True),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"]),
sa.ForeignKeyConstraint(["user_id"], ["users.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"notifications",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("title", sa.Text(), nullable=True),
sa.Column("content", sa.Text(), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"]),
sa.ForeignKeyConstraint(["user_id"], ["users.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"submissions",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.Column("ip", sa.String(length=46), nullable=True),
sa.Column("provided", sa.Text(), nullable=True),
sa.Column("type", sa.String(length=32), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["challenge_id"], ["challenges.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"tracking",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("type", sa.String(length=32), nullable=True),
sa.Column("ip", sa.String(length=46), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(["user_id"], ["users.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"unlocks",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.Column("target", sa.Integer(), nullable=True),
sa.Column("date", sa.DateTime(), nullable=True),
sa.Column("type", sa.String(length=32), nullable=True),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"]),
sa.ForeignKeyConstraint(["user_id"], ["users.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"solves",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("team_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["challenge_id"], ["challenges.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["id"], ["submissions.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["team_id"], ["teams.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("challenge_id", "team_id"),
sa.UniqueConstraint("challenge_id", "user_id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("solves")
op.drop_table("unlocks")
op.drop_table("tracking")
op.drop_table("submissions")
op.drop_table("notifications")
op.drop_table("awards")
op.drop_table("users")
op.drop_table("tags")
op.drop_table("hints")
op.drop_table("flags")
op.drop_table("files")
op.drop_table("dynamic_challenge")
op.drop_table("teams")
op.drop_table("pages")
op.drop_table("config")
op.drop_table("challenges")
# ### end Alembic commands ###

View File

@@ -0,0 +1,51 @@
"""Add Brackets table
Revision ID: 9889b8c53673
Revises: 5c4996aeb2cb
Create Date: 2024-01-25 03:17:52.734753
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = "9889b8c53673"
down_revision = "5c4996aeb2cb"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"brackets",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=255), nullable=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("type", sa.String(length=80), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.add_column("teams", sa.Column("bracket_id", sa.Integer(), nullable=True))
op.create_foreign_key(
None, "teams", "brackets", ["bracket_id"], ["id"], ondelete="SET NULL"
)
op.drop_column("teams", "bracket")
op.add_column("users", sa.Column("bracket_id", sa.Integer(), nullable=True))
op.create_foreign_key(
None, "users", "brackets", ["bracket_id"], ["id"], ondelete="SET NULL"
)
op.drop_column("users", "bracket")
def downgrade():
op.add_column(
"users", sa.Column("bracket", mysql.VARCHAR(length=32), nullable=True)
)
op.drop_constraint(None, "users", type_="foreignkey")
op.drop_column("users", "bracket_id")
op.add_column(
"teams", sa.Column("bracket", mysql.VARCHAR(length=32), nullable=True)
)
op.drop_constraint(None, "teams", type_="foreignkey")
op.drop_column("teams", "bracket_id")
op.drop_table("brackets")

View File

@@ -0,0 +1,23 @@
"""Add description column to tokens table
Revision ID: 9e6f6578ca84
Revises: 0def790057c1
Create Date: 2023-06-21 23:22:34.179636
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "9e6f6578ca84"
down_revision = "0def790057c1"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("tokens", sa.Column("description", sa.Text(), nullable=True))
def downgrade():
op.drop_column("tokens", "description")

View File

@@ -0,0 +1,26 @@
"""Add link_target to Pages
Revision ID: a02c5bf43407
Revises: 9889b8c53673
Create Date: 2024-02-01 13:11:53.076825
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "a02c5bf43407"
down_revision = "9889b8c53673"
branch_labels = None
depends_on = None
def upgrade():
op.add_column(
"pages", sa.Column("link_target", sa.String(length=80), nullable=True)
)
def downgrade():
op.drop_column("pages", "link_target")

View File

@@ -0,0 +1,46 @@
"""add theme code injections to configs
Revision ID: a03403986a32
Revises: 080d29b15cd3
Create Date: 2020-02-13 01:10:16.430424
"""
from alembic import op # noqa: I001
from sqlalchemy.sql import column, table
from CTFd.models import db
# revision identifiers, used by Alembic.
revision = "a03403986a32"
down_revision = "080d29b15cd3"
branch_labels = None
depends_on = None
configs_table = table(
"config", column("id", db.Integer), column("key", db.Text), column("value", db.Text)
)
def upgrade():
connection = op.get_bind()
css = connection.execute(
configs_table.select().where(configs_table.c.key == "css").limit(1)
).fetchone()
if css and css.value:
new_css = "<style>\n" + css.value + "\n</style>"
config = connection.execute(
configs_table.select().where(configs_table.c.key == "theme_header").limit(1)
).fetchone()
if config:
# Do not overwrite existing theme_header value
pass
else:
connection.execute(
configs_table.insert().values(key="theme_header", value=new_css)
)
def downgrade():
pass

View File

@@ -0,0 +1,23 @@
"""add title to hint
Revision ID: a49ad66aa0f1
Revises: 4fe3eeed9a9d
Create Date: 2025-02-10 14:45:00.933880
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "a49ad66aa0f1"
down_revision = "4fe3eeed9a9d"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("hints", sa.Column("title", sa.String(length=80), nullable=True))
def downgrade():
op.drop_column("hints", "title")

View File

@@ -0,0 +1,298 @@
"""Add ondelete cascade to foreign keys
Revision ID: b295b033364d
Revises: b5551cd26764
Create Date: 2019-05-03 19:26:57.746887
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "b295b033364d"
down_revision = "b5551cd26764"
branch_labels = None
depends_on = None
def upgrade():
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith("mysql"):
op.drop_constraint("awards_ibfk_1", "awards", type_="foreignkey")
op.drop_constraint("awards_ibfk_2", "awards", type_="foreignkey")
op.create_foreign_key(
"awards_ibfk_1", "awards", "teams", ["team_id"], ["id"], ondelete="CASCADE"
)
op.create_foreign_key(
"awards_ibfk_2", "awards", "users", ["user_id"], ["id"], ondelete="CASCADE"
)
op.drop_constraint("files_ibfk_1", "files", type_="foreignkey")
op.create_foreign_key(
"files_ibfk_1",
"files",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("flags_ibfk_1", "flags", type_="foreignkey")
op.create_foreign_key(
"flags_ibfk_1",
"flags",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("hints_ibfk_1", "hints", type_="foreignkey")
op.create_foreign_key(
"hints_ibfk_1",
"hints",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("tags_ibfk_1", "tags", type_="foreignkey")
op.create_foreign_key(
"tags_ibfk_1",
"tags",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("team_captain_id", "teams", type_="foreignkey")
op.create_foreign_key(
"team_captain_id",
"teams",
"users",
["captain_id"],
["id"],
ondelete="SET NULL",
)
op.drop_constraint("tracking_ibfk_1", "tracking", type_="foreignkey")
op.create_foreign_key(
"tracking_ibfk_1",
"tracking",
"users",
["user_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("unlocks_ibfk_1", "unlocks", type_="foreignkey")
op.drop_constraint("unlocks_ibfk_2", "unlocks", type_="foreignkey")
op.create_foreign_key(
"unlocks_ibfk_1",
"unlocks",
"teams",
["team_id"],
["id"],
ondelete="CASCADE",
)
op.create_foreign_key(
"unlocks_ibfk_2",
"unlocks",
"users",
["user_id"],
["id"],
ondelete="CASCADE",
)
elif url.startswith("postgres"):
op.drop_constraint("awards_team_id_fkey", "awards", type_="foreignkey")
op.drop_constraint("awards_user_id_fkey", "awards", type_="foreignkey")
op.create_foreign_key(
"awards_team_id_fkey",
"awards",
"teams",
["team_id"],
["id"],
ondelete="CASCADE",
)
op.create_foreign_key(
"awards_user_id_fkey",
"awards",
"users",
["user_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("files_challenge_id_fkey", "files", type_="foreignkey")
op.create_foreign_key(
"files_challenge_id_fkey",
"files",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("flags_challenge_id_fkey", "flags", type_="foreignkey")
op.create_foreign_key(
"flags_challenge_id_fkey",
"flags",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("hints_challenge_id_fkey", "hints", type_="foreignkey")
op.create_foreign_key(
"hints_challenge_id_fkey",
"hints",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("tags_challenge_id_fkey", "tags", type_="foreignkey")
op.create_foreign_key(
"tags_challenge_id_fkey",
"tags",
"challenges",
["challenge_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("team_captain_id", "teams", type_="foreignkey")
op.create_foreign_key(
"team_captain_id",
"teams",
"users",
["captain_id"],
["id"],
ondelete="SET NULL",
)
op.drop_constraint("tracking_user_id_fkey", "tracking", type_="foreignkey")
op.create_foreign_key(
"tracking_user_id_fkey",
"tracking",
"users",
["user_id"],
["id"],
ondelete="CASCADE",
)
op.drop_constraint("unlocks_team_id_fkey", "unlocks", type_="foreignkey")
op.drop_constraint("unlocks_user_id_fkey", "unlocks", type_="foreignkey")
op.create_foreign_key(
"unlocks_team_id_fkey",
"unlocks",
"teams",
["team_id"],
["id"],
ondelete="CASCADE",
)
op.create_foreign_key(
"unlocks_user_id_fkey",
"unlocks",
"users",
["user_id"],
["id"],
ondelete="CASCADE",
)
def downgrade():
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith("mysql"):
op.drop_constraint("unlocks_ibfk_1", "unlocks", type_="foreignkey")
op.drop_constraint("unlocks_ibfk_2", "unlocks", type_="foreignkey")
op.create_foreign_key("unlocks_ibfk_1", "unlocks", "teams", ["team_id"], ["id"])
op.create_foreign_key("unlocks_ibfk_2", "unlocks", "users", ["user_id"], ["id"])
op.drop_constraint("tracking_ibfk_1", "tracking", type_="foreignkey")
op.create_foreign_key(
"tracking_ibfk_1", "tracking", "users", ["user_id"], ["id"]
)
op.drop_constraint("team_captain_id", "teams", type_="foreignkey")
op.create_foreign_key(
"team_captain_id", "teams", "users", ["captain_id"], ["id"]
)
op.drop_constraint("tags_ibfk_1", "tags", type_="foreignkey")
op.create_foreign_key(
"tags_ibfk_1", "tags", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("hints_ibfk_1", "hints", type_="foreignkey")
op.create_foreign_key(
"hints_ibfk_1", "hints", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("flags_ibfk_1", "flags", type_="foreignkey")
op.create_foreign_key(
"flags_ibfk_1", "flags", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("files_ibfk_1", "files", type_="foreignkey")
op.create_foreign_key(
"files_ibfk_1", "files", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("awards_ibfk_1", "awards", type_="foreignkey")
op.drop_constraint("awards_ibfk_2", "awards", type_="foreignkey")
op.create_foreign_key("awards_ibfk_1", "awards", "teams", ["team_id"], ["id"])
op.create_foreign_key("awards_ibfk_2", "awards", "users", ["user_id"], ["id"])
elif url.startswith("postgres"):
op.drop_constraint("unlocks_team_id_fkey", "unlocks", type_="foreignkey")
op.drop_constraint("unlocks_user_id_fkey", "unlocks", type_="foreignkey")
op.create_foreign_key(
"unlocks_team_id_fkey", "unlocks", "teams", ["team_id"], ["id"]
)
op.create_foreign_key(
"unlocks_user_id_fkey", "unlocks", "users", ["user_id"], ["id"]
)
op.drop_constraint("tracking_user_id_fkey", "tracking", type_="foreignkey")
op.create_foreign_key(
"tracking_user_id_fkey", "tracking", "users", ["user_id"], ["id"]
)
op.drop_constraint("team_captain_id", "teams", type_="foreignkey")
op.create_foreign_key(
"team_captain_id", "teams", "users", ["captain_id"], ["id"]
)
op.drop_constraint("tags_challenge_id_fkey", "tags", type_="foreignkey")
op.create_foreign_key(
"tags_challenge_id_fkey", "tags", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("hints_challenge_id_fkey", "hints", type_="foreignkey")
op.create_foreign_key(
"hints_challenge_id_fkey", "hints", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("flags_challenge_id_fkey", "flags", type_="foreignkey")
op.create_foreign_key(
"flags_challenge_id_fkey", "flags", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("files_challenge_id_fkey", "files", type_="foreignkey")
op.create_foreign_key(
"files_challenge_id_fkey", "files", "challenges", ["challenge_id"], ["id"]
)
op.drop_constraint("awards_team_id_fkey", "awards", type_="foreignkey")
op.drop_constraint("awards_user_id_fkey", "awards", type_="foreignkey")
op.create_foreign_key(
"awards_team_id_fkey", "awards", "teams", ["team_id"], ["id"]
)
op.create_foreign_key(
"awards_user_id_fkey", "awards", "users", ["user_id"], ["id"]
)

View File

@@ -0,0 +1,57 @@
"""Add captain column to Teams
Revision ID: b5551cd26764
Revises: 4e4d5a9ea000
Create Date: 2019-04-12 00:29:08.021141
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.sql import column, table
from CTFd.models import db
# revision identifiers, used by Alembic.
revision = "b5551cd26764"
down_revision = "4e4d5a9ea000"
branch_labels = None
depends_on = None
teams_table = table("teams", column("id", db.Integer), column("captain_id", db.Integer))
users_table = table("users", column("id", db.Integer), column("team_id", db.Integer))
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("teams", sa.Column("captain_id", sa.Integer(), nullable=True))
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith("sqlite") is False:
op.create_foreign_key(
"team_captain_id", "teams", "users", ["captain_id"], ["id"]
)
connection = op.get_bind()
for team in connection.execute(teams_table.select()):
users = connection.execute(
users_table.select()
.where(users_table.c.team_id == team.id)
.order_by(users_table.c.id)
.limit(1)
)
for user in users:
connection.execute(
teams_table.update()
.where(teams_table.c.id == team.id)
.values(captain_id=user.id)
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint("team_captain_id", "teams", type_="foreignkey")
op.drop_column("teams", "captain_id")
# ### end Alembic commands ###

View File

@@ -0,0 +1,46 @@
"""Add topics and challenge_topics tables
Revision ID: ef87d69ec29a
Revises: 07dfbe5e1edc
Create Date: 2021-07-29 23:22:39.345426
"""
from alembic import op # noqa: I001
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "ef87d69ec29a"
down_revision = "07dfbe5e1edc"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"topics",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("value", sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("value"),
)
op.create_table(
"challenge_topics",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("challenge_id", sa.Integer(), nullable=True),
sa.Column("topic_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["challenge_id"], ["challenges.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["topic_id"], ["topics.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("challenge_topics")
op.drop_table("topics")
# ### end Alembic commands ###

View File

@@ -0,0 +1,30 @@
"""Add logic column to Challenges
Revision ID: f73a96c97449
Revises: 62bf576b2cd3
Create Date: 2025-08-08 21:49:23.417694
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "f73a96c97449"
down_revision = "62bf576b2cd3"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"challenges", sa.Column("logic", sa.String(length=80), nullable=False)
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("challenges", "logic")
# ### end Alembic commands ###