diff --git a/.env.example b/.env.example index 2de8301..b012966 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ MOCK_AUTH=true/false DB_USER=MyDBUser DB_PASSWORD=MyDBPassword DB_HOST=MyDBHost -DB_NAME=iptv_updater +DB_NAME=iptv_manager FREEDNS_User=MyFreeDNSUsername FREEDNS_Password=MyFreeDNSPassword diff --git a/.gitea/workflows/aws_deploy_on_push.yml b/.gitea/workflows/aws_deploy_on_push.yml index 0f196c5..3439f5e 100644 --- a/.gitea/workflows/aws_deploy_on_push.yml +++ b/.gitea/workflows/aws_deploy_on_push.yml @@ -58,7 +58,7 @@ jobs: run: | INSTANCE_IDS=$(aws ec2 describe-instances \ --region us-east-2 \ - --filters "Name=tag:Name,Values=IptvUpdaterStack/IptvUpdaterInstance" \ + --filters "Name=tag:Name,Values=IptvManagerStack/IptvManagerInstance" \ "Name=instance-state-name,Values=running" \ --query "Reservations[].Instances[].InstanceId" \ --output text) @@ -69,11 +69,11 @@ jobs: --instance-ids "$INSTANCE_ID" \ --document-name "AWS-RunShellScript" \ --parameters 'commands=[ - "cd /home/ec2-user/iptv-updater-aws", + "cd /home/ec2-user/iptv-manager-service", "git pull", "pip3 install -r requirements.txt", "alembic upgrade head", - "sudo systemctl restart iptv-updater" + "sudo systemctl restart iptv-manager" ]' done diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a97673a..3b18a4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.2 + rev: v0.11.12 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/.vscode/settings.json b/.vscode/settings.json index 7609979..2403527 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { + "python.terminal.activateEnvironment": true, + "python.terminal.activateEnvInCurrentTerminal": true, "editor.formatOnSave": true, "editor.defaultFormatter": "charliermarsh.ruff", "ruff.importStrategy": "fromEnvironment", @@ -31,10 +33,13 @@ "cluflogo", "clulogo", "cpulogo", + "crond", + "cronie", "cuflgo", "CUNF", "cunflogo", "cuulogo", + "datname", "deadstreams", "delenv", "delogo", @@ -50,6 +55,7 @@ "freedns", "fullchain", "gitea", + "httpx", "iptv", "isort", "KHTML", @@ -61,7 +67,9 @@ "ondelete", "onupdate", "passlib", + "PGPASSWORD", "poolclass", + "psql", "psycopg", "pycache", "pycodestyle", @@ -82,6 +90,7 @@ "testdb", "testpass", "testpaths", + "testuser", "uflogo", "umlogo", "usefixtures", diff --git a/README.md b/README.md index 200540b..46e3b82 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# IPTV Updater AWS +# IPTV Manager Service -An automated IPTV playlist and EPG updater service deployed on AWS infrastructure using CDK. +An automated IPTV playlist and EPG manager service deployed on AWS infrastructure using CDK. ## Overview @@ -25,7 +25,7 @@ This project provides a service for automatically updating IPTV playlists and El ```bash git clone -cd iptv-updater-aws +cd iptv-manager-service ``` 2. Copy the example environment file: @@ -144,13 +144,13 @@ scripts/ # Utility scripts for deployment and management The following environment variables are required: -| Variable | Description | -|----------|-------------| -| FREEDNS_User | FreeDNS username | -| FREEDNS_Password | FreeDNS password | -| DOMAIN_NAME | Your domain name | -| SSH_PUBLIC_KEY | SSH public key for EC2 access | -| REPO_URL | Repository URL | +| Variable | Description | +| ----------------- | ------------------------------------ | +| FREEDNS_User | FreeDNS username | +| FREEDNS_Password | FreeDNS password | +| DOMAIN_NAME | Your domain name | +| SSH_PUBLIC_KEY | SSH public key for EC2 access | +| REPO_URL | Repository URL | | LETSENCRYPT_EMAIL | Email for Let's Encrypt certificates | ## Security Notes diff --git a/alembic/env.py b/alembic/env.py index 8426f76..40d482e 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -15,7 +15,8 @@ config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name) -# Setup target metadata for autogenerate support +# add your model's MetaData object here +# for 'autogenerate' support target_metadata = Base.metadata # Override sqlalchemy.url with dynamic credentials diff --git a/alembic/versions/036879e47172_add_priority_and_in_use_fields.py b/alembic/versions/036879e47172_add_priority_and_in_use_fields.py deleted file mode 100644 index d185281..0000000 --- a/alembic/versions/036879e47172_add_priority_and_in_use_fields.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Add priority and in_use fields - -Revision ID: 036879e47172 -Revises: -Create Date: 2025-05-26 19:21:32.285656 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '036879e47172' -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: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - # 1. Create priorities table if not exists - if not op.get_bind().engine.dialect.has_table(op.get_bind(), 'priorities'): - op.create_table('priorities', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('description', sa.String(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - - # 2. Insert default priorities (skip if already exists) - op.execute(""" - INSERT INTO priorities (id, description) - VALUES (100, 'High'), (200, 'Medium'), (300, 'Low') - ON CONFLICT (id) DO NOTHING - """) - # Add new columns with temporary nullable=True - op.add_column('channels_urls', sa.Column('in_use', sa.Boolean(), nullable=True)) - op.add_column('channels_urls', sa.Column('priority_id', sa.Integer(), nullable=True)) - - # Set default values - op.execute("UPDATE channels_urls SET in_use = false, priority_id = 100") - - # Convert to NOT NULL - op.alter_column('channels_urls', 'in_use', nullable=False) - op.alter_column('channels_urls', 'priority_id', nullable=False) - op.create_foreign_key(None, 'channels_urls', 'priorities', ['priority_id'], ['id']) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('channels_urls_priority_id_fkey', 'channels_urls', type_='foreignkey') - op.drop_column('channels_urls', 'priority_id') - op.drop_column('channels_urls', 'in_use') - op.drop_table('priorities') - # ### end Alembic commands ### diff --git a/alembic/versions/95b61a92455a_create_initial_tables.py b/alembic/versions/95b61a92455a_create_initial_tables.py new file mode 100644 index 0000000..9cdec40 --- /dev/null +++ b/alembic/versions/95b61a92455a_create_initial_tables.py @@ -0,0 +1,79 @@ +"""create initial tables + +Revision ID: 95b61a92455a +Revises: +Create Date: 2025-05-29 14:42:16.239587 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '95b61a92455a' +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: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('channels', + sa.Column('id', sa.UUID(), nullable=False), + sa.Column('tvg_id', sa.String(), nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('group_title', sa.String(), nullable=False), + sa.Column('tvg_name', sa.String(), nullable=True), + sa.Column('tvg_logo', sa.String(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('group_title', 'name', name='uix_group_title_name') + ) + op.create_table('priorities', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('description', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('channels_urls', + sa.Column('id', sa.UUID(), nullable=False), + sa.Column('channel_id', sa.UUID(), nullable=False), + sa.Column('url', sa.String(), nullable=False), + sa.Column('in_use', sa.Boolean(), nullable=False), + sa.Column('priority_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['channel_id'], ['channels.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['priority_id'], ['priorities.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + # Seed initial priorities + op.bulk_insert( + sa.Table( + 'priorities', + sa.MetaData(), + sa.Column('id', sa.Integer), + sa.Column('description', sa.String), + ), + [ + {'id': 100, 'description': 'High'}, + {'id': 200, 'description': 'Medium'}, + {'id': 300, 'description': 'Low'}, + ] + ) + + +def downgrade() -> None: + """Downgrade schema.""" + # Remove seeded priorities + op.execute("DELETE FROM priorities WHERE id IN (100, 200, 300);") + + # Drop tables + op.drop_table('channels_urls') + op.drop_table('priorities') + op.drop_table('channels') \ No newline at end of file diff --git a/app.py b/app.py index 495aab6..547b3c0 100644 --- a/app.py +++ b/app.py @@ -3,7 +3,7 @@ import os import aws_cdk as cdk -from infrastructure.stack import IptvUpdaterStack +from infrastructure.stack import IptvManagerStack app = cdk.App() @@ -31,9 +31,9 @@ if missing_vars: f"Missing required environment variables: {', '.join(missing_vars)}" ) -IptvUpdaterStack( +IptvManagerStack( app, - "IptvUpdaterStack", + "IptvManagerStack", freedns_user=freedns_user, freedns_password=freedns_password, domain_name=domain_name, diff --git a/app/main.py b/app/main.py index 205660c..1e79974 100644 --- a/app/main.py +++ b/app/main.py @@ -15,8 +15,8 @@ async def lifespan(app: FastAPI): app = FastAPI( lifespan=lifespan, - title="IPTV Updater API", - description="API for IPTV Updater service", + title="IPTV Manager API", + description="API for IPTV Manager service", version="1.0.0", ) @@ -60,7 +60,7 @@ app.openapi = custom_openapi @app.get("/") async def root(): - return {"message": "IPTV Updater API"} + return {"message": "IPTV Manager API"} # Include routers diff --git a/app/utils/database.py b/app/utils/database.py index be56764..c5dea92 100644 --- a/app/utils/database.py +++ b/app/utils/database.py @@ -19,16 +19,16 @@ def get_db_credentials(): ssm = boto3.client("ssm", region_name=AWS_REGION) try: - host = ssm.get_parameter(Name="/iptv-updater/DB_HOST", WithDecryption=True)[ + host = ssm.get_parameter(Name="/iptv-manager/DB_HOST", WithDecryption=True)[ "Parameter" ]["Value"] - user = ssm.get_parameter(Name="/iptv-updater/DB_USER", WithDecryption=True)[ + user = ssm.get_parameter(Name="/iptv-manager/DB_USER", WithDecryption=True)[ "Parameter" ]["Value"] password = ssm.get_parameter( - Name="/iptv-updater/DB_PASSWORD", WithDecryption=True + Name="/iptv-manager/DB_PASSWORD", WithDecryption=True )["Parameter"]["Value"] - dbname = ssm.get_parameter(Name="/iptv-updater/DB_NAME", WithDecryption=True)[ + dbname = ssm.get_parameter(Name="/iptv-manager/DB_NAME", WithDecryption=True)[ "Parameter" ]["Value"] return f"postgresql://{user}:{password}@{host}/{dbname}" diff --git a/docker/docker-compose-db.yml b/docker/docker-compose-db.yml index c2f853e..ff0d311 100644 --- a/docker/docker-compose-db.yml +++ b/docker/docker-compose-db.yml @@ -3,10 +3,11 @@ version: '3.8' services: postgres: image: postgres:13 + container_name: postgres environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: iptv_updater + POSTGRES_DB: iptv_manager ports: - "5432:5432" volumes: diff --git a/docker/docker-compose-local.yml b/docker/docker-compose-local.yml index 1ac2961..29adfba 100644 --- a/docker/docker-compose-local.yml +++ b/docker/docker-compose-local.yml @@ -6,7 +6,7 @@ services: environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: iptv_updater + POSTGRES_DB: iptv_manager ports: - "5432:5432" volumes: @@ -20,7 +20,7 @@ services: DB_USER: postgres DB_PASSWORD: postgres DB_HOST: postgres - DB_NAME: iptv_updater + DB_NAME: iptv_manager MOCK_AUTH: "true" ports: - "8000:8000" diff --git a/infrastructure/stack.py b/infrastructure/stack.py index e4679d2..dd91299 100644 --- a/infrastructure/stack.py +++ b/infrastructure/stack.py @@ -9,7 +9,7 @@ from aws_cdk import aws_ssm as ssm from constructs import Construct -class IptvUpdaterStack(Stack): +class IptvManagerStack(Stack): def __init__( self, scope: Construct, @@ -27,7 +27,7 @@ class IptvUpdaterStack(Stack): # Create VPC vpc = ec2.Vpc( self, - "IptvUpdaterVPC", + "IptvManagerVPC", max_azs=2, # Need at least 2 AZs for RDS subnet group nat_gateways=0, # No NAT Gateway to stay in free tier subnet_configuration=[ @@ -44,7 +44,7 @@ class IptvUpdaterStack(Stack): # Security Group security_group = ec2.SecurityGroup( - self, "IptvUpdaterSG", vpc=vpc, allow_all_outbound=True + self, "IptvManagerSG", vpc=vpc, allow_all_outbound=True ) security_group.add_ingress_rule( @@ -66,18 +66,18 @@ class IptvUpdaterStack(Stack): "Allow PostgreSQL traffic for tunneling", ) - # Key pair for IPTV Updater instance + # Key pair for IPTV Manager instance key_pair = ec2.KeyPair( self, - "IptvUpdaterKeyPair", - key_pair_name="iptv-updater-key", + "IptvManagerKeyPair", + key_pair_name="iptv-manager-key", public_key_material=ssh_public_key, ) # Create IAM role for EC2 role = iam.Role( self, - "IptvUpdaterRole", + "IptvManagerRole", assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), ) @@ -114,7 +114,7 @@ class IptvUpdaterStack(Stack): # EC2 Instance instance = ec2.Instance( self, - "IptvUpdaterInstance", + "IptvManagerInstance", vpc=vpc, vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC), instance_type=ec2.InstanceType.of( @@ -132,7 +132,7 @@ class IptvUpdaterStack(Stack): # Option: 2: Create Elastic IP (not free tier compatible) # eip = ec2.CfnEIP( - # self, "IptvUpdaterEIP", + # self, "IptvManagerEIP", # domain="vpc", # instance_id=instance.instance_id # ) @@ -140,8 +140,8 @@ class IptvUpdaterStack(Stack): # Add Cognito User Pool user_pool = cognito.UserPool( self, - "IptvUpdaterUserPool", - user_pool_name="iptv-updater-users", + "IptvManagerUserPool", + user_pool_name="iptv-manager-users", self_sign_up_enabled=False, # Only admins can create users password_policy=cognito.PasswordPolicy( min_length=8, @@ -156,7 +156,7 @@ class IptvUpdaterStack(Stack): # Add App Client with the correct callback URL client = user_pool.add_client( - "IptvUpdaterClient", + "IptvManagerClient", access_token_validity=Duration.minutes(60), id_token_validity=Duration.minutes(60), refresh_token_validity=Duration.days(1), @@ -171,8 +171,8 @@ class IptvUpdaterStack(Stack): # Add domain for hosted UI domain = user_pool.add_domain( - "IptvUpdaterDomain", - cognito_domain=cognito.CognitoDomainOptions(domain_prefix="iptv-updater"), + "IptvManagerDomain", + cognito_domain=cognito.CognitoDomainOptions(domain_prefix="iptv-manager"), ) # Read the userdata script with proper path resolution @@ -226,7 +226,7 @@ class IptvUpdaterStack(Stack): # Create RDS PostgreSQL instance (free tier compatible - db.t3.micro) db = rds.DatabaseInstance( self, - "IptvUpdaterDB", + "IptvManagerDB", engine=rds.DatabaseInstanceEngine.postgres( version=rds.PostgresEngineVersion.VER_13 ), @@ -240,7 +240,7 @@ class IptvUpdaterStack(Stack): security_groups=[rds_sg], allocated_storage=10, max_allocated_storage=10, - database_name="iptv_updater", + database_name="iptv_manager", removal_policy=RemovalPolicy.DESTROY, deletion_protection=False, publicly_accessible=False, # Avoid public IPv4 charges @@ -255,25 +255,25 @@ class IptvUpdaterStack(Stack): ssm.StringParameter( self, "DBHostParam", - parameter_name="/iptv-updater/DB_HOST", + parameter_name="/iptv-manager/DB_HOST", string_value=db.db_instance_endpoint_address, ) ssm.StringParameter( self, "DBNameParam", - parameter_name="/iptv-updater/DB_NAME", - string_value="iptv_updater", + parameter_name="/iptv-manager/DB_NAME", + string_value="iptv_manager", ) ssm.StringParameter( self, "DBUserParam", - parameter_name="/iptv-updater/DB_USER", + parameter_name="/iptv-manager/DB_USER", string_value=db.secret.secret_value_from_json("username").to_string(), ) ssm.StringParameter( self, "DBPassParam", - parameter_name="/iptv-updater/DB_PASSWORD", + parameter_name="/iptv-manager/DB_PASSWORD", string_value=db.secret.secret_value_from_json("password").to_string(), ) diff --git a/infrastructure/userdata.sh b/infrastructure/userdata.sh index c99288f..c5d0b7e 100644 --- a/infrastructure/userdata.sh +++ b/infrastructure/userdata.sh @@ -2,7 +2,7 @@ # Update system and install required packages dnf update -y -dnf install -y python3-pip git cronie nginx certbot python3-certbot-nginx +dnf install -y python3-pip git cronie nginx certbot python3-certbot-nginx postgresql awscli # Start and enable crond service systemctl start crond @@ -11,27 +11,48 @@ systemctl enable crond cd /home/ec2-user git clone ${REPO_URL} -cd iptv-updater-aws +cd iptv-manager-service # Install Python packages with --ignore-installed to prevent conflicts with RPM packages pip3 install --ignore-installed -r requirements.txt +# Retrieve DB credentials from SSM Parameter Store +export DB_HOST=$(aws ssm get-parameter --name "/iptv-manager/DB_HOST" --query "Parameter.Value" --output text) +export DB_NAME=$(aws ssm get-parameter --name "/iptv-manager/DB_NAME" --query "Parameter.Value" --output text) +export DB_USER=$(aws ssm get-parameter --name "/iptv-manager/DB_USER" --query "Parameter.Value" --output text) +export DB_PASSWORD=$(aws ssm get-parameter --name "/iptv-manager/DB_PASSWORD" --query "Parameter.Value" --output text) + +# Set PGPASSWORD for psql to use +export PGPASSWORD=$DB_PASSWORD + +# Wait for PostgreSQL to be ready +echo "Waiting for PostgreSQL to start..." +until psql -h $DB_HOST -U $DB_USER -d postgres -c '\q'; do + sleep 1 +done +echo "PostgreSQL is ready." + +# Create database if it does not exist +DB_EXISTS=$(psql -h $DB_HOST -U $DB_USER -d postgres -tc "SELECT 1 FROM pg_database WHERE datname = '$DB_NAME';") +if [ -z "$DB_EXISTS" ]; then + echo "Creating database $DB_NAME..." + psql -h $DB_HOST -U $DB_USER -d postgres -c "CREATE DATABASE $DB_NAME;" + echo "Database $DB_NAME created." +fi + # Run database migrations alembic upgrade head -# Seed initial priorities -python3 -c "from app.utils.database import SessionLocal; from app.models.db import Priority; db = SessionLocal(); db.add_all([Priority(id=100, description='High'), Priority(id=200, description='Medium'), Priority(id=300, description='Low')]); db.commit()" - # Create systemd service file -cat << 'EOF' > /etc/systemd/system/iptv-updater.service +cat << 'EOF' > /etc/systemd/system/iptv-manager.service [Unit] -Description=IPTV Updater Service +Description=IPTV Manager Service After=network.target [Service] Type=simple User=ec2-user -WorkingDirectory=/home/ec2-user/iptv-updater-aws +WorkingDirectory=/home/ec2-user/iptv-manager-service ExecStart=/usr/local/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 EnvironmentFile=/etc/environment Restart=always @@ -56,7 +77,7 @@ sudo mkdir -p /etc/nginx/ssl --reloadcmd "service nginx force-reload" # Create nginx config -cat << EOF > /etc/nginx/conf.d/iptvUpdater.conf +cat << EOF > /etc/nginx/conf.d/iptvManager.conf server { listen 80; server_name ${DOMAIN_NAME} *.${DOMAIN_NAME}; @@ -83,5 +104,5 @@ EOF # Start nginx service systemctl enable nginx systemctl start nginx -systemctl enable iptv-updater -systemctl start iptv-updater \ No newline at end of file +systemctl enable iptv-manager +systemctl start iptv-manager \ No newline at end of file diff --git a/pytest.ini b/pytest.ini index 8c64b37..dcd1711 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,12 +5,21 @@ python_functions = test_* asyncio_mode = auto filterwarnings = ignore::DeprecationWarning:botocore.auth + ignore:The 'app' shortcut is now deprecated:DeprecationWarning:httpx._client # Coverage configuration addopts = --cov=app --cov-report=term-missing +# Test environment variables +env = + MOCK_AUTH=true + DB_USER=test_user + DB_PASSWORD=test_password + DB_HOST=localhost + DB_NAME=iptv_manager_test + # Test markers markers = slow: mark tests as slow running diff --git a/requirements.txt b/requirements.txt index f702cc0..390d76c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,8 @@ psycopg2-binary==2.9.9 alembic==1.16.1 pytest==8.1.1 pytest-asyncio==0.23.6 -pytest-mock==3.12.0 \ No newline at end of file +pytest-mock==3.12.0 +pytest-cov==4.1.0 +pytest-env==1.1.1 +httpx==0.27.0 +pre-commit \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 9c8a485..ba68fe0 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -25,7 +25,7 @@ cdk deploy --app="python3 ${PWD}/app.py" # Update application on running instances INSTANCE_IDS=$(aws ec2 describe-instances \ --region us-east-2 \ - --filters "Name=tag:Name,Values=IptvUpdaterStack/IptvUpdaterInstance" \ + --filters "Name=tag:Name,Values=IptvManagerStack/IptvManagerInstance" \ "Name=instance-state-name,Values=running" \ --query "Reservations[].Instances[].InstanceId" \ --output text) @@ -35,7 +35,7 @@ for INSTANCE_ID in $INSTANCE_IDS; do aws ssm send-command \ --instance-ids "$INSTANCE_ID" \ --document-name "AWS-RunShellScript" \ - --parameters '{"commands":["cd /home/ec2-user/iptv-updater-aws && git pull && pip3 install -r requirements.txt && alembic upgrade head && sudo systemctl restart iptv-updater"]}' \ + --parameters '{"commands":["cd /home/ec2-user/iptv-manager-service && git pull && pip3 install -r requirements.txt && alembic upgrade head && sudo systemctl restart iptv-manager"]}' \ --no-cli-pager \ --no-paginate done diff --git a/scripts/install.sh b/scripts/install.sh index 2cbcb7e..8bb2fba 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -10,10 +10,4 @@ pre-commit install-hooks pre-commit autoupdate # Verify pytest setup -python3 -m pytest - -# Initialize and run database migrations -alembic upgrade head - -# Seed initial data -python3 -c "from app.utils.database import SessionLocal; from app.models.db import Priority; db = SessionLocal(); db.add_all([Priority(id=100, description='High'), Priority(id=200, description='Medium'), Priority(id=300, description='Low')]); db.commit()" \ No newline at end of file +python3 -m pytest \ No newline at end of file diff --git a/scripts/start_local_dev.sh b/scripts/start_local_dev.sh index 3cb4095..ffebea2 100755 --- a/scripts/start_local_dev.sh +++ b/scripts/start_local_dev.sh @@ -1,21 +1,26 @@ #!/bin/bash +set -e # Start PostgreSQL docker-compose -f docker/docker-compose-db.yml up -d -# Set mock auth and database environment variables +# Set environment variables export MOCK_AUTH=true +export DB_HOST=localhost export DB_USER=postgres export DB_PASSWORD=postgres -export DB_HOST=localhost -export DB_NAME=iptv_updater +export DB_NAME=iptv_manager + +echo "Ensuring database $DB_NAME exists using conditional DDL..." +PGPASSWORD=$DB_PASSWORD docker exec -i postgres psql -U $DB_USER <<< "SELECT 'CREATE DATABASE $DB_NAME' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$DB_NAME')\gexec" +echo "Database $DB_NAME check complete." # Run database migrations alembic upgrade head # Start FastAPI nohup uvicorn app.main:app --host 127.0.0.1 --port 8000 > app.log 2>&1 & -echo $! > iptv-updater.pid +echo $! > iptv-manager.pid echo "Services started:" echo "- PostgreSQL running on localhost:5432" diff --git a/scripts/stop_local_dev.sh b/scripts/stop_local_dev.sh index 1f0b737..94fd62d 100755 --- a/scripts/stop_local_dev.sh +++ b/scripts/stop_local_dev.sh @@ -1,9 +1,9 @@ #!/bin/bash # Stop FastAPI -if [ -f iptv-updater.pid ]; then - kill $(cat iptv-updater.pid) - rm iptv-updater.pid +if [ -f iptv-manager.pid ]; then + kill $(cat iptv-manager.pid) + rm iptv-manager.pid echo "Stopped FastAPI" fi diff --git a/tests/test_main.py b/tests/test_main.py index ff5ba5d..3ea1730 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -16,7 +16,7 @@ def test_root_endpoint(client): """Test root endpoint returns expected message""" response = client.get("/") assert response.status_code == 200 - assert response.json() == {"message": "IPTV Updater API"} + assert response.json() == {"message": "IPTV Manager API"} def test_openapi_schema_generation(client):