fixed migrations

This commit is contained in:
2025-06-02 16:11:56 +02:00
parent c95a1c456b
commit 4dbaa27cba
98 changed files with 399 additions and 109 deletions

View File

@@ -1,33 +1,57 @@
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
postgresql-client \
build-essential \
libpq-dev \
curl \
netcat-traditional \
&& rm -rf /var/lib/apt/lists/*
# Create a non-root user
RUN useradd -m -u 1000 celery
# Set working directory
WORKDIR /app
# Copy requirements first to leverage Docker cache
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application
# Copy application code
COPY . .
# Create migrations directory if it doesn't exist
RUN mkdir -p migrations/versions
# Create necessary directories and set permissions
RUN mkdir -p /app/uploads /app/static/uploads && \
chown -R celery:celery /app
# Make entrypoint script executable
RUN chmod +x entrypoint.sh
# Create and set up startup script
RUN echo '#!/bin/bash\n\
echo "Waiting for database..."\n\
while ! nc -z db 5432; do\n\
sleep 0.1\n\
done\n\
echo "Database is ready!"\n\
\n\
echo "Waiting for Redis..."\n\
while ! nc -z redis 6379; do\n\
sleep 0.1\n\
done\n\
echo "Redis is ready!"\n\
\n\
echo "Running database migrations..."\n\
flask db upgrade\n\
\n\
echo "Creating admin user..."\n\
flask create-admin\n\
\n\
echo "Starting application..."\n\
exec "$@"' > /app/start.sh && \
chmod +x /app/start.sh && \
chown celery:celery /app/start.sh
# Set environment variables
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# Switch to non-root user
USER celery
# Expose the port the app runs on
EXPOSE 5000
# Use the entrypoint script
ENTRYPOINT ["./entrypoint.sh"]
# Set entrypoint
ENTRYPOINT ["/app/start.sh"]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
app.py
View File

@@ -3,7 +3,7 @@ from flask import Flask, send_from_directory, jsonify
from flask_migrate import Migrate
from dotenv import load_dotenv
import os
from models import User
from models import User, SiteSettings
from flask_wtf.csrf import generate_csrf
from routes.room_files import room_files_bp
from routes.user import user_bp
@@ -45,7 +45,12 @@ def create_app():
@app.context_processor
def inject_config():
return dict(config=app.config)
site_settings = SiteSettings.query.first()
if not site_settings:
site_settings = SiteSettings()
db.session.add(site_settings)
db.session.commit()
return dict(config=app.config, site_settings=site_settings)
# User loader for Flask-Login
@login_manager.user_loader
@@ -111,7 +116,12 @@ def create_app():
# Create default email templates if they don't exist
with app.app_context():
create_default_templates()
try:
# Ensure database tables exist
db.create_all()
create_default_templates()
except Exception as e:
print(f"Warning: Could not create default templates: {e}")
return app

View File

@@ -3,6 +3,7 @@ version: '3.8'
services:
web:
build: .
command: gunicorn --bind 0.0.0.0:5000 app:app
ports:
- "10335:5000"
environment:
@@ -59,17 +60,18 @@ services:
celery_worker:
build: .
command: celery -A celery_worker.celery worker --loglevel=info
environment:
- FLASK_APP=app.py
- FLASK_ENV=production
- DATABASE_URL=postgresql://postgres:postgres@db:5432/docupulse
- REDIS_URL=redis://redis:6379/0
volumes:
- .:/app
environment:
- FLASK_APP=app.py
- FLASK_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/docupulse
- REDIS_URL=redis://redis:6379/0
depends_on:
- web
- redis
- db
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "celery", "-A", "celery_worker.celery", "inspect", "ping"]

Binary file not shown.

View File

@@ -7,6 +7,7 @@ Create Date: 2025-06-02 14:10:54.033943
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,18 +19,23 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('key_value_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('key', sa.String(length=100), nullable=False),
sa.Column('value', sa.Text(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('key')
)
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'key_value_settings' not in tables:
op.create_table('key_value_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('key', sa.String(length=100), nullable=False),
sa.Column('value', sa.Text(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('key')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('key_value_settings')
# ### end Alembic commands ###
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-26 14:00:05.521776
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -2,11 +2,12 @@
Revision ID: 1c297825e3a9
Revises:
Create Date: 2025-05-23 08:39:40.494853
Create Date: 2025-06-02 13:26:30.353000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -17,17 +18,24 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=150), nullable=False),
sa.Column('email', sa.String(length=150), nullable=False),
sa.Column('password_hash', sa.String(length=128), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('username')
)
# ### end Alembic commands ###
# Check if the table exists before creating it
conn = op.get_bind()
inspector = sa.inspect(conn)
if 'user' not in inspector.get_table_names():
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'user' not in tables:
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=150), nullable=False),
sa.Column('email', sa.String(length=150), nullable=False),
sa.Column('password_hash', sa.String(length=128), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('username')
)
def downgrade():

View File

@@ -7,6 +7,7 @@ Create Date: 2025-06-02 09:04:39.972021
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('mails',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'mails' not in tables:
op.create_table('mails',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('recipient', sa.String(length=150), nullable=False),
sa.Column('subject', sa.String(length=200), nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 16:10:53.731035
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 21:44:58.832286
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,17 +19,22 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('room_member_permissions',
sa.Column('room_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('can_view', sa.Boolean(), nullable=False),
sa.Column('can_upload', sa.Boolean(), nullable=False),
sa.Column('can_delete', sa.Boolean(), nullable=False),
sa.Column('can_share', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['room_id'], ['room.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('room_id', 'user_id')
)
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'room_member_permissions' not in tables:
op.create_table('room_member_permissions',
sa.Column('room_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('can_view', sa.Boolean(), nullable=False),
sa.Column('can_upload', sa.Boolean(), nullable=False),
sa.Column('can_delete', sa.Boolean(), nullable=False),
sa.Column('can_share', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(['room_id'], ['room.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('room_id', 'user_id')
)
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 21:27:17.497481
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,15 +19,24 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('room_members',
sa.Column('room_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['room_id'], ['room.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('room_id', 'user_id')
)
with op.batch_alter_table('room', schema=None) as batch_op:
batch_op.drop_column('is_private')
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'room_members' not in tables:
op.create_table('room_members',
sa.Column('room_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['room_id'], ['room.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('room_id', 'user_id')
)
# Check if is_private column exists before dropping it
columns = [col['name'] for col in inspector.get_columns('room')]
if 'is_private' in columns:
with op.batch_alter_table('room', schema=None) as batch_op:
batch_op.drop_column('is_private')
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 21:25:27.880150
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,16 +19,21 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('room',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('created_by', sa.Integer(), nullable=False),
sa.Column('is_private', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'room' not in tables:
op.create_table('room',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('created_by', sa.Integer(), nullable=False),
sa.Column('is_private', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 09:24:23.926302
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,12 +19,21 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = inspect(conn)
columns = [col['name'] for col in inspector.get_columns('user')]
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.add_column(sa.Column('phone', sa.String(length=20), nullable=True))
batch_op.add_column(sa.Column('company', sa.String(length=100), nullable=True))
batch_op.add_column(sa.Column('position', sa.String(length=100), nullable=True))
batch_op.add_column(sa.Column('notes', sa.Text(), nullable=True))
batch_op.add_column(sa.Column('is_active', sa.Boolean(), nullable=True))
if 'phone' not in columns:
batch_op.add_column(sa.Column('phone', sa.String(length=20), nullable=True))
if 'company' not in columns:
batch_op.add_column(sa.Column('company', sa.String(length=100), nullable=True))
if 'position' not in columns:
batch_op.add_column(sa.Column('position', sa.String(length=100), nullable=True))
if 'notes' not in columns:
batch_op.add_column(sa.Column('notes', sa.Text(), nullable=True))
if 'is_active' not in columns:
batch_op.add_column(sa.Column('is_active', sa.Boolean(), nullable=True))
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-06-02 08:25:48.241102
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -24,7 +25,12 @@ def upgrade():
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('template_variables',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'template_variables' not in tables:
op.create_table('template_variables',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('notification_type', sa.VARCHAR(length=50), autoincrement=False, nullable=False),
sa.Column('variable_name', sa.VARCHAR(length=50), autoincrement=False, nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-24 10:07:02.159730
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('room_file',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'room_file' not in tables:
op.create_table('room_file',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('room_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-24 18:14:38.320999
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:05:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.sql import text

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-25 10:03:03.423064
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-26 10:42:17.287566
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:15:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.sql import text

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-25 21:16:39.683736
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('site_settings',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'site_settings' not in tables:
op.create_table('site_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('primary_color', sa.String(length=7), nullable=True),
sa.Column('secondary_color', sa.String(length=7), nullable=True),
@@ -31,7 +37,12 @@ def upgrade():
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('color_settings',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'color_settings' not in tables:
op.create_table('color_settings',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('primary_color', sa.VARCHAR(length=7), autoincrement=False, nullable=True),
sa.Column('secondary_color', sa.VARCHAR(length=7), autoincrement=False, nullable=True),

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# Create conversation table first
op.create_table('conversation',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'conversation' not in tables:
op.create_table('conversation',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
@@ -29,7 +35,12 @@ def upgrade():
)
# Create conversation_members table
op.create_table('conversation_members',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'conversation_members' not in tables:
op.create_table('conversation_members',
sa.Column('conversation_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['conversation_id'], ['conversation.id'], ),
@@ -38,7 +49,12 @@ def upgrade():
)
# Create message table
op.create_table('message',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'message' not in tables:
op.create_table('message',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('content', sa.Text(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:45:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
revision = 'add_deleted_by_to_room_file'

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:30:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
revision = 'add_deleted_column_to_room_file'

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -17,7 +18,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('trashed_file',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'trashed_file' not in tables:
op.create_table('trashed_file',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('room_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-24 08:36:03.426879
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-26 11:14:05.629795
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-24 12:32:19.239241
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -8,6 +8,7 @@ Create Date: 2025-05-23 19:28:16.977187
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
revision = 'c21f243b3640'
@@ -19,8 +20,13 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('contact')
conn = op.get_bind()
inspector = inspect(conn)
columns = [col['name'] for col in inspector.get_columns('user')]
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.add_column(sa.Column('profile_picture', sa.String(length=255), nullable=True))
if 'profile_picture' not in columns:
batch_op.add_column(sa.Column('profile_picture', sa.String(length=255), nullable=True))
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 16:00:09.905001
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,8 +19,13 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = inspect(conn)
columns = [col['name'] for col in inspector.get_columns('user')]
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.add_column(sa.Column('last_name', sa.String(length=150), nullable=True))
if 'last_name' not in columns:
batch_op.add_column(sa.Column('last_name', sa.String(length=150), nullable=True))
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-06-01 20:09:08.019884
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('email_templates',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'email_templates' not in tables:
op.create_table('email_templates',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('subject', sa.String(length=200), nullable=False),
@@ -54,7 +60,12 @@ def downgrade():
type_=postgresql.JSONB(astext_type=sa.Text()),
existing_nullable=True)
op.create_table('notification',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'notification' not in tables:
op.create_table('notification',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False),
sa.Column('title', sa.VARCHAR(length=255), autoincrement=False, nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-25 21:08:37.158900
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('color_settings',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'color_settings' not in tables:
op.create_table('color_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('primary_color', sa.String(length=7), nullable=True),
sa.Column('secondary_color', sa.String(length=7), nullable=True),

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -17,7 +18,12 @@ depends_on = None
def upgrade():
# Create user_starred_file table
op.create_table('user_starred_file',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'user_starred_file' not in tables:
op.create_table('user_starred_file',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('file_id', sa.Integer(), nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 08:45:00.693155
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-23 08:55:10.537722
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -35,9 +36,17 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
# Check if columns exist before adding them
conn = op.get_bind()
inspector = inspect(conn)
columns = [col['name'] for col in inspector.get_columns('user')]
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_admin', sa.Boolean(), nullable=True))
batch_op.add_column(sa.Column('created_at', sa.DateTime(), nullable=True))
if 'is_admin' not in columns:
batch_op.add_column(sa.Column('is_admin', sa.Boolean(), nullable=True))
if 'created_at' not in columns:
batch_op.add_column(sa.Column('created_at', sa.DateTime(), nullable=True))
# ### end Alembic commands ###

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-26 15:00:18.557702
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
@@ -18,7 +19,12 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('message_attachment',
conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if 'message_attachment' not in tables:
op.create_table('message_attachment',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('message_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),

View File

@@ -7,6 +7,7 @@ Create Date: 2025-05-26 10:52:32.572951
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -7,13 +7,6 @@ from utils import log_event, create_notification, get_unread_count
auth_bp = Blueprint('auth', __name__)
@auth_bp.context_processor
def inject_unread_notifications():
if current_user.is_authenticated:
unread_count = get_unread_count(current_user.id)
return {'unread_notifications': unread_count}
return {'unread_notifications': 0}
def require_password_change(f):
@wraps(f)
def decorated_function(*args, **kwargs):
@@ -24,6 +17,13 @@ def require_password_change(f):
return decorated_function
def init_routes(auth_bp):
@auth_bp.context_processor
def inject_unread_notifications():
if current_user.is_authenticated:
unread_count = get_unread_count(current_user.id)
return {'unread_notifications': unread_count}
return {'unread_notifications': 0}
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:

22
start.sh Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
echo "Waiting for database..."
while ! nc -z db 5432; do
sleep 0.1
done
echo "Database is ready!"
echo "Waiting for Redis..."
while ! nc -z redis 6379; do
sleep 0.1
done
echo "Redis is ready!"
echo "Running database migrations..."
flask db upgrade
echo "Creating admin user..."
flask create-admin
echo "Starting application..."
exec "$@"

71
update_migrations.py Normal file
View File

@@ -0,0 +1,71 @@
import os
import re
from pathlib import Path
def update_migration_file(file_path):
with open(file_path, 'r') as f:
content = f.read()
# Skip if already has the pattern
if 'from sqlalchemy import inspect' in content:
return False
# Add import if not present
if 'import sqlalchemy as sa' in content:
content = content.replace('import sqlalchemy as sa', 'import sqlalchemy as sa\nfrom sqlalchemy import inspect')
else:
content = content.replace('from alembic import op', 'from alembic import op\nfrom sqlalchemy import inspect')
# Find all create_table operations
create_table_pattern = r'op\.create_table\([\'"](\w+)[\'"]'
tables = re.findall(create_table_pattern, content)
for table in tables:
# Create the check pattern with proper indentation
check_pattern = f""" conn = op.get_bind()
inspector = inspect(conn)
tables = inspector.get_table_names()
if '{table}' not in tables:"""
# Find the create_table line and its indentation
create_table_line = f"op.create_table('{table}'"
if create_table_line in content:
# Get the indentation of the create_table line
lines = content.split('\n')
for i, line in enumerate(lines):
if create_table_line in line:
indent = len(line) - len(line.lstrip())
# Add the check before the create_table with matching indentation
check_lines = check_pattern.split('\n')
check_lines = [' ' * indent + line.lstrip() for line in check_lines]
check_pattern = '\n'.join(check_lines)
# Add extra indentation to the create_table line
create_table_line = ' ' * (indent + 4) + create_table_line
# Replace in the content
content = content.replace(line, f"{check_pattern}\n{create_table_line}")
# Write back the updated content
with open(file_path, 'w') as f:
f.write(content)
return True
def main():
migrations_dir = Path('migrations/versions')
updated_files = []
for file in migrations_dir.glob('*.py'):
if file.name != '__init__.py':
if update_migration_file(file):
updated_files.append(file.name)
if updated_files:
print("Updated the following migration files:")
for file in updated_files:
print(f"- {file}")
else:
print("No files needed updating.")
if __name__ == '__main__':
main()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.