Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

SECRET_KEY=change-me
DEBUG=True
ALLOWED_HOSTS=
ALLOWED_HOSTS
HS_CODE_SEARCH_THRESHOLD=0.1

# db
DATABASE_URL=postgres://hsuser:hspass@localhost:5432/hsdb
55 changes: 55 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Deploy Production

on:
workflow_run:
workflows: ["CI"]
branches: [main]
types:
- completed

jobs:
deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' }}

runs-on: ubuntu-latest

steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}

script: |
set -e

cd /Hs_codes_api

echo "Pulling latest images..."
docker compose pull

echo "Starting updated containers..."
docker compose up -d

echo "Waiting for application..."
sleep 15

echo "Running migrations..."
docker compose exec -T web python manage.py migrate --noinput

echo "Checking health endpoint..."

for i in $(seq 1 20); do
if curl -fsS http://localhost:8001/api/v1/health/ > /dev/null; then
echo "Health check passed"
exit 0
fi

echo "Waiting for healthy container..."
sleep 5
done

echo "Health check failed"
exit 1
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ dist/
build/
*.egg-info/

celerybeat-schedule
celerybeat-schedule
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Builder Stage
FROM python:3.12-slim AS builder

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev

COPY requirements.txt .

RUN pip install --upgrade pip
RUN pip install --prefix=/install -r requirements.txt


# Runtime Stage
FROM python:3.12-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN groupadd -r appuser && useradd -r -g appuser appuser

COPY --from=builder /install /usr/local

COPY . .

RUN chown -R appuser:appuser /app

USER appuser

EXPOSE 8001

ENTRYPOINT ["/app/entrypoint.sh"]
113 changes: 97 additions & 16 deletions app/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Generated by Django 6.0.5 on 2026-05-31 16:13
# Generated by Django 6.0.5 on 2026-06-01 14:40

import django.contrib.auth.models
import django.contrib.auth.validators
import django.contrib.postgres.indexes
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
from django.contrib.postgres.operations import TrigramExtension


class Migration(migrations.Migration):
Expand All @@ -15,6 +18,38 @@ class Migration(migrations.Migration):
]

operations = [
TrigramExtension(),

migrations.CreateModel(
name="Category",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=25)),
],
),
migrations.CreateModel(
name="HsCodeFile",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("hs_code_file", models.FileField(upload_to="hs_code")),
],
),
migrations.CreateModel(
name="User",
fields=[
Expand All @@ -30,9 +65,7 @@ class Migration(migrations.Migration):
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
models.DateTimeField(blank=True, null=True, verbose_name="last login"),
),
(
"is_superuser",
Expand All @@ -59,21 +92,15 @@ class Migration(migrations.Migration):
),
(
"first_name",
models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
models.CharField(blank=True, max_length=150, verbose_name="first name"),
),
(
"last_name",
models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
models.CharField(blank=True, max_length=150, verbose_name="last name"),
),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
models.EmailField(blank=True, max_length=254, verbose_name="email address"),
),
(
"is_staff",
Expand All @@ -93,8 +120,14 @@ class Migration(migrations.Migration):
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined"),
),
(
"role",
models.CharField(
choices=[("Admin", "admin"), ("Staff", "staff")],
default="admin",
max_length=5,
),
),
(
Expand Down Expand Up @@ -129,4 +162,52 @@ class Migration(migrations.Migration):
("objects", django.contrib.auth.models.UserManager()),
],
),
]
migrations.CreateModel(
name="HsCode",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("hs_code", models.CharField(max_length=20)),
("description", models.TextField()),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"category",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="app.category",
),
),
(
"hs_code_file",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="app.hscodefile",
),
),
],
options={
"indexes": [
django.contrib.postgres.indexes.GinIndex(
fields=["hs_code"],
name="hscode_hs_code_trgm_idx",
opclasses=["gin_trgm_ops"],
),
django.contrib.postgres.indexes.GinIndex(
fields=["description"],
name="hscode_description_trgm_idx",
opclasses=["gin_trgm_ops"],
),
],
"constraints": [
models.UniqueConstraint(fields=("hs_code",), name="unique_hs_code")
],
},
),
]
17 changes: 17 additions & 0 deletions app/migrations/0002_remove_hscode_category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 6.0.5 on 2026-06-01 16:32

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("app", "0001_initial"),
]

operations = [
migrations.RemoveField(
model_name="hscode",
name="category",
),
]
23 changes: 0 additions & 23 deletions app/migrations/0002_user_role.py

This file was deleted.

76 changes: 0 additions & 76 deletions app/migrations/0003_category_alter_user_role_hscode.py

This file was deleted.

Loading