From 04d9136b967c6bb003267f8536fcaca4340162d8 Mon Sep 17 00:00:00 2001 From: c-d-p Date: Sat, 26 Apr 2025 13:23:45 +0200 Subject: [PATCH] [V0.5] Working application with all 4 screens as of yet. --- .gitignore | 2 + backend/.env | 6 +- backend/alembic.ini | 2 +- backend/alembic/env.py | 40 ++++++-- ...a_add_all_day_column_to_calendar_events.py | 94 ++++++++++++++++++ .../core/__pycache__/config.cpython-312.pyc | Bin 1299 -> 1421 bytes .../core/__pycache__/database.cpython-312.pyc | Bin 1866 -> 2180 bytes backend/core/config.py | 10 +- backend/core/database.py | 1 + backend/docker-compose.yml | 25 ++--- backend/main.py | 2 +- .../admin/__pycache__/api.cpython-312.pyc | Bin 1661 -> 1661 bytes .../__pycache__/dependencies.cpython-312.pyc | Bin 1334 -> 1334 bytes .../nlp/__pycache__/models.cpython-312.pyc | Bin 1360 -> 1360 bytes interfaces/nativeapp/.env | 1 + 15 files changed, 154 insertions(+), 29 deletions(-) create mode 100644 backend/alembic/versions/a34d847510da_add_all_day_column_to_calendar_events.py create mode 100644 interfaces/nativeapp/.env diff --git a/.gitignore b/.gitignore index fe019eb..57d5da0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # backend backend/env backend/.env +backend/db +backend/redis_data # frontend interfaces/nativeapp/node_modules diff --git a/backend/.env b/backend/.env index 6530144..3dc3cfd 100644 --- a/backend/.env +++ b/backend/.env @@ -1,5 +1,7 @@ -POSTGRES_USER = "maia" -POSTGRES_PASSWORD = "maia" +DB_HOST = "db" +DB_USER = "maia" +DB_PASSWORD = "maia" +DB_NAME = "maia" PEPPER = "LsD7%" JWT_SECRET_KEY="1c8cf3ca6972b365f8108dad247e61abdcb6faff5a6c8ba00cb6fa17396702bf" GOOGLE_API_KEY="AIzaSyBrte_mETZJce8qE6cRTSz_fHOjdjlShBk" diff --git a/backend/alembic.ini b/backend/alembic.ini index 64ceafc..34f435e 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -64,7 +64,7 @@ version_path_separator = os # output_encoding = utf-8 # sqlalchemy.url = postgresql://user:pass@localhost/dbname -sqlalchemy.url = postgresql://maia:maia@db:5432/maia +# sqlalchemy.url = postgresql://maia:maia@db:5432/maia [post_write_hooks] # post_write_hooks defines scripts or Python functions that are run diff --git a/backend/alembic/env.py b/backend/alembic/env.py index f78bcd6..2e5bbd5 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -4,6 +4,7 @@ from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool +from sqlalchemy import create_engine # Add create_engine import from alembic import context @@ -25,6 +26,29 @@ config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name) +# --- Construct DB URL from environment variables --- +# Use environment variables similar to docker-compose +db_user = os.getenv("POSTGRES_USER", "maia") # Default to 'maia' if not set +db_password = os.getenv("POSTGRES_PASSWORD", "maia") # Default to 'maia' if not set +db_host = os.getenv("DB_HOST", "db") # Default to 'db' service name +db_port = os.getenv("DB_PORT", "5432") # Default to '5432' +db_name = os.getenv("DB_NAME", "maia") # Default to 'maia' + +# Construct the URL, falling back to alembic.ini if needed +url = os.getenv("DB_URL") +if not url: + # Try constructing from parts if DB_URL isn't set + url = f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" + # As a final fallback, use the URL from alembic.ini + config_url = config.get_main_option("sqlalchemy.url") + if not url and config_url: + url = config_url + +# Update the config object so engine_from_config can potentially use it, +# though we'll primarily use the constructed 'url' directly. +config.set_main_option("sqlalchemy.url", url) +# ---------------------------------------------------- + # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel @@ -51,9 +75,8 @@ def run_migrations_offline() -> None: script output. """ - url = config.get_main_option("sqlalchemy.url") context.configure( - url=url, + url=url, # Use the constructed URL target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"}, @@ -70,11 +93,14 @@ def run_migrations_online() -> None: and associate a connection with the context. """ - connectable = engine_from_config( - config.get_section(config.config_ini_section, {}), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) + # Create engine directly using the constructed URL + connectable = create_engine(url, poolclass=pool.NullPool) + # Original approach using engine_from_config: + # 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) diff --git a/backend/alembic/versions/a34d847510da_add_all_day_column_to_calendar_events.py b/backend/alembic/versions/a34d847510da_add_all_day_column_to_calendar_events.py new file mode 100644 index 0000000..71e9dd9 --- /dev/null +++ b/backend/alembic/versions/a34d847510da_add_all_day_column_to_calendar_events.py @@ -0,0 +1,94 @@ +"""Add all_day column to calendar_events + +Revision ID: a34d847510da +Revises: 9a82960db482 +Create Date: 2025-04-26 11:09:35.400748 + +""" +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 = 'a34d847510da' +down_revision: Union[str, None] = '9a82960db482' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('calendar_events') + op.drop_table('users') + op.drop_index('ix_todos_id', table_name='todos') + op.drop_index('ix_todos_task', table_name='todos') + op.drop_table('todos') + op.drop_table('token_blacklist') + op.drop_index('ix_chat_messages_id', table_name='chat_messages') + op.drop_index('ix_chat_messages_user_id', table_name='chat_messages') + op.drop_table('chat_messages') + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('chat_messages', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column('sender', postgresql.ENUM('USER', 'AI', name='messagesender'), autoincrement=False, nullable=False), + sa.Column('text', sa.TEXT(), autoincrement=False, nullable=False), + sa.Column('timestamp', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='chat_messages_user_id_fkey'), + sa.PrimaryKeyConstraint('id', name='chat_messages_pkey') + ) + op.create_index('ix_chat_messages_user_id', 'chat_messages', ['user_id'], unique=False) + op.create_index('ix_chat_messages_id', 'chat_messages', ['id'], unique=False) + op.create_table('token_blacklist', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('token', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('expires_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name='token_blacklist_pkey'), + sa.UniqueConstraint('token', name='token_blacklist_token_key') + ) + op.create_table('todos', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('task', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column('date', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), + sa.Column('remind', sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column('complete', sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column('owner_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['owner_id'], ['users.id'], name='todos_owner_id_fkey'), + sa.PrimaryKeyConstraint('id', name='todos_pkey') + ) + op.create_index('ix_todos_task', 'todos', ['task'], unique=False) + op.create_index('ix_todos_id', 'todos', ['id'], unique=False) + op.create_table('users', + sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('users_id_seq'::regclass)"), autoincrement=True, nullable=False), + sa.Column('uuid', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('username', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('role', postgresql.ENUM('ADMIN', 'USER', name='userrole'), autoincrement=False, nullable=False), + sa.Column('hashed_password', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name='users_pkey'), + sa.UniqueConstraint('username', name='users_username_key'), + sa.UniqueConstraint('uuid', name='users_uuid_key'), + postgresql_ignore_search_path=False + ) + op.create_table('calendar_events', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('title', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column('description', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('start', postgresql.TIMESTAMP(), autoincrement=False, nullable=False), + sa.Column('end', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), + sa.Column('location', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('tags', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True), + sa.Column('color', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='calendar_events_user_id_fkey'), + sa.PrimaryKeyConstraint('id', name='calendar_events_pkey') + ) + # ### end Alembic commands ### diff --git a/backend/core/__pycache__/config.cpython-312.pyc b/backend/core/__pycache__/config.cpython-312.pyc index 7ed7cd50b296425f50da3994e6effa72f6263368..bf23a90ebcb13317bee7246d48d3118c1b6afe88 100644 GIT binary patch delta 583 zcmY+A%SyvQ6ox07wqx7$8f}9&Xm#Pnx|C7`wY4r%}wEZ^$=~LwNUYF3Tw+G4rD z!)is{=9GWCZy;Y?^MY}F)toEXCe>cuBo zxI319!Aduhyz-DVE0bx4wV4nZ1sMa31H24$(6U_`*7cfgH{6`sZ3VoU9aiEdo^NlI6WoR@wOl{=!`i^(S| zt!0+(#FCJ1m&KLYiN9GCJHJ1X{9pHpb9)VAziuv&x%QKoO^ymm>-Yn)*ZTqr%GcRb H%rEy1Q}~Aq delta 421 zcmeC>p3KF2nwOW00SNq)xiS__n9 zKpqE}#~H;1=5c{|U{&0377v`o3t}-bxHF{iwJ@acuVw;rfgp;zQb1Gi7DsSuNl9j2 zdU2IjL4I*bdQoa|VUB{8zJ6|EW}+2{aLCC|PRz*wN?Mtk7#o2lHj6S%Wn>is8Y4M* zJ+pMZ43JeM2PFJ7d5T0p98nM<1|ryi#4YCHlA>Gu@$rdydHE%YC7Jno#qse)>>zng z5Wx*rl$lpj#0%nrj4F}_5nz*LK`d4n!2x6xhXaXLh6h4+4Q?II%$Hg0CRefeFv?6m z&f=yg4pVuHqZsNtG{b&z*yQG?l;)(`6=_bkX0?(KWt8lw`N9CCJ{mFbh;&;ua)7M{ E02=jNDoA5W;ca1w60Ky_2CE#O&0fA_btZ{A4Cp z9Y*oV`mAdi6(-+hRTBa$W&z*G)Yy zntEO^@j9V+!sEJ6%tfD=3*NC8V&g8vr(ciHzZjo?A+F#;LE(j>s_#JK7@e6uLZ}Zw bOBr35fRwN+Qv*i}|0kBos_cp^AlCo@GYf&< delta 272 zcmZn>JjJJRnwOW00SKa!xiS{AFfcp@abSQI%J^KyFi|5!t%V_qH-#NY@}+R3vZu17 zu%@u4F-rm!r7@*&wy;F;SF&nyO*UeT-n^7?5)-Q!P=BOQG@e0|@fKTZ zUV3I;YB3{_t5765xt~>xRTjvWo4kruhf!qmb=I|vvXfic)PzKVqPN)6Q%mAgl8Tgp z9JR@N*@77bCJV9$I0^vyMIg5qNrMQm4IpE{3X6n6ED;a^GUOMBO>TZlX-=wLkp_?p Wvboq}@(p%PrcZ2><2e*rKq>)Ghcn>- diff --git a/backend/core/config.py b/backend/core/config.py index eee5bff..dce8491 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -6,8 +6,14 @@ DOTENV_PATH = os.path.join(os.path.dirname(__file__), "../.env") class Settings(BaseSettings): - # Database settings - reads DB_URL from environment or .env - DB_URL: str = "postgresql://maia:maia@localhost:5432/maia" + # Database settings - reads from environment or .env + DB_PORT: int = 5432 + DB_NAME: str = "maia" + DB_HOST: str + DB_USER: str + DB_PASSWORD: str + + DB_URL: str = "" # Redis settings - reads REDIS_URL from environment or .env, also used for Celery. REDIS_URL: str = "redis://localhost:6379/0" diff --git a/backend/core/database.py b/backend/core/database.py index 396734e..3425083 100644 --- a/backend/core/database.py +++ b/backend/core/database.py @@ -9,6 +9,7 @@ Base = declarative_base() # Used for models _engine = None _SessionLocal = None +settings.DB_URL = f"postgresql://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}" def get_engine(): global _engine diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index f834afc..223e02a 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -1,4 +1,8 @@ # docker-compose.yml + +################### +### DEV COMPOSE ### +################### services: # ----- Backend API (Uvicorn/FastAPI/Django etc.) ----- api: @@ -11,9 +15,6 @@ services: - .:/app ports: - "8000:8000" - environment: - - DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/maia - - REDIS_URL=redis://redis:6379/0 depends_on: - db - redis @@ -32,9 +33,6 @@ services: command: celery -A core.celery_app worker --loglevel=info volumes: - .:/app - environment: - - DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/maia - - REDIS_URL=redis://redis:6379/0 depends_on: - db - redis @@ -49,11 +47,11 @@ services: image: postgres:15 # Use a specific version container_name: MAIA-DB volumes: - - postgres_data:/var/lib/postgresql/data # Persist data using a named volume + - ./db:/var/lib/postgresql/data # Persist data using a named volume environment: - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - POSTGRES_DB=maia + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=${DB_NAME} env_file: - ./.env networks: @@ -65,16 +63,11 @@ services: image: redis:7 # Use a specific version container_name: MAIA-Redis volumes: - - redis_data:/data + - ./redis_data:/data networks: - maia_network restart: unless-stopped -# ----- Volumes Definition ----- -volumes: - postgres_data: # Define the named volume for PostgreSQL - redis_data: # Define the named volume for Redis - # ----- Network Definition ----- networks: maia_network: # Define a custom bridge network diff --git a/backend/main.py b/backend/main.py index 23523af..71d32c1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -10,7 +10,6 @@ logging.getLogger("passlib").setLevel(logging.ERROR) # fix bc package logging i def lifespan_factory() -> Callable[[FastAPI], _AsyncGeneratorContextManager[Any]]: - @asynccontextmanager async def lifespan(app: FastAPI): # Base.metadata.drop_all(bind=get_engine()) @@ -29,6 +28,7 @@ app.add_middleware( CORSMiddleware, allow_origins=[ "https://maia.depaoli.id.au", + "http://localhost:8081", ], allow_credentials=True, allow_methods=["*"], diff --git a/backend/modules/admin/__pycache__/api.cpython-312.pyc b/backend/modules/admin/__pycache__/api.cpython-312.pyc index 65877700d94111986506a50642bba43176a6444c..63ae70ccaeb378c885bdff2201a8fdc4000a60a2 100644 GIT binary patch delta 19 Zcmey%^OuL~G%qg~0}wb%BfPG%qg~0}y0S+Q{X`3IH_O1hD`B delta 19 Zcmcb>b%BfPG%qg~0}w