Refactor Docker configuration and API endpoints

- Merged production and development Docker configurations
- Updated Dockerfile to use multi-stage build
- Removed separate Dockerfile.prod
- Modified docker-compose.yml for production settings
- Added new API endpoints for user filtering by ID card
- Updated serializers and views for Atoll and Island management
- Enhanced user and atoll-related filters and views
This commit is contained in:
i701 2025-01-24 11:43:18 +05:00
parent f6f77bb0e5
commit c1fc07e3e2
Signed by: i701
GPG Key ID: 54A0DA1E26D8E587
9 changed files with 118 additions and 137 deletions

View File

@ -1,5 +1,9 @@
###########
# BUILDER #
###########
# pull official base image
FROM python:3.11.4-slim-buster
FROM python:3.11.4-slim-buster AS builder
# set work directory
WORKDIR /app
@ -9,20 +13,59 @@ ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install system dependencies
RUN apt-get update && apt-get install -y netcat
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
# lint
RUN pip install --upgrade pip
COPY . /app/
# install python dependencies
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
#########
# FINAL #
#########
# pull official base image
FROM python:3.11.4-slim-buster
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup --system app && adduser --system --group app
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/api
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/staticfiles
RUN chmod -R 777 $APP_HOME/staticfiles
WORKDIR $APP_HOME
# install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends netcat
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
RUN pip install --no-cache /wheels/*
# copy entrypoint.sh
COPY ./entrypoint.sh .
RUN sed -i 's/\r$//g' /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# copy entrypoint.prod.sh
COPY ./entrypoint.prod.sh .
RUN sed -i 's/\r$//g' $APP_HOME/entrypoint.prod.sh
RUN chmod +x $APP_HOME/entrypoint.prod.sh
# copy project
COPY . .
COPY . $APP_HOME
# run entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
# run entrypoint.prod.sh
ENTRYPOINT ["/home/app/api/entrypoint.prod.sh"]

View File

@ -1,71 +0,0 @@
###########
# BUILDER #
###########
# pull official base image
FROM python:3.11.4-slim-buster AS builder
# set work directory
WORKDIR /app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
# lint
RUN pip install --upgrade pip
COPY . /app/
# install python dependencies
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
#########
# FINAL #
#########
# pull official base image
FROM python:3.11.4-slim-buster
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup --system app && adduser --system --group app
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/api
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/staticfiles
RUN chmod -R 777 $APP_HOME/staticfiles
WORKDIR $APP_HOME
# install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends netcat
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --upgrade pip
RUN pip install --no-cache /wheels/*
# copy entrypoint.prod.sh
COPY ./entrypoint.prod.sh .
RUN sed -i 's/\r$//g' $APP_HOME/entrypoint.prod.sh
RUN chmod +x $APP_HOME/entrypoint.prod.sh
# copy project
COPY . $APP_HOME
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
# run entrypoint.prod.sh
ENTRYPOINT ["/home/app/api/entrypoint.prod.sh"]

View File

@ -13,4 +13,8 @@ class UserFilter(django_filters.FilterSet):
"username",
"last_name",
"first_name",
"email",
"is_active",
"id_card",
"mobile",
]

View File

@ -46,9 +46,19 @@ class CustomReadOnlyUserSerializer(serializers.ModelSerializer):
"username",
"mobile",
"address",
"id_card",
)
class CustomReadOnlyUserByIDCardSerializer(serializers.ModelSerializer):
"""serializer for the user object"""
class Meta: # type: ignore
model = User
# fields = "__all__"
fields = ("id_card",)
class UserSerializer(serializers.ModelSerializer):
"""serializer for the user object"""
@ -93,13 +103,16 @@ class KnoxTokenSerializer(serializers.ModelSerializer):
fields = "__all__"
class AtollSerializer(serializers.ModelSerializer):
class Meta: # type: ignore
model = Atoll
fields = "__all__"
class IslandSerializer(serializers.ModelSerializer):
class Meta: # type: ignore
model = Island
fields = "__all__"
class AtollSerializer(serializers.ModelSerializer):
islands = IslandSerializer(many=True, read_only=True)
class Meta: # type: ignore
model = Atoll
fields = "__all__"
depth = 2

View File

@ -11,10 +11,12 @@ from .views import (
UserDetailAPIView,
healthcheck,
test_email,
ListCreateAtollView,
ListAtollView,
CreateAtollView,
RetrieveUpdateDestroyAtollView,
ListCreateIslandView,
RetrieveUpdateDestroyIslandView,
ListUserByIDCardView,
)
@ -28,9 +30,11 @@ urlpatterns = [
# path("auth/", CustomAuthToken.as_view()),
path("users/", ListUserView.as_view(), name="users"),
path("users/<int:pk>/", UserDetailAPIView.as_view(), name="user-detail"),
path("users/idcard/", ListUserByIDCardView.as_view(), name="users-idcard"),
path("healthcheck/", healthcheck, name="healthcheck"),
path("test/", test_email, name="testemail"),
path("atolls/", ListCreateAtollView.as_view(), name="atolls"),
path("atolls/", ListAtollView.as_view(), name="atolls"),
path("atolls/new/", CreateAtollView.as_view(), name="atoll-new"),
path(
"atolls/<int:pk>/",
RetrieveUpdateDestroyAtollView.as_view(),

View File

@ -28,6 +28,7 @@ from .serializers import (
AuthSerializer,
CustomUserSerializer,
CustomReadOnlyUserSerializer,
CustomReadOnlyUserByIDCardSerializer,
)
@ -195,6 +196,16 @@ class ListUserView(StaffEditorPermissionMixin, generics.ListAPIView):
queryset = User.objects.all()
class ListUserByIDCardView(generics.ListAPIView):
# Create user API view
permission_classes = (permissions.AllowAny,)
serializer_class = CustomReadOnlyUserByIDCardSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = "__all__"
filterset_class = UserFilter
queryset = User.objects.all()
class UserDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = CustomReadOnlyUserSerializer
@ -228,7 +239,7 @@ def test_email(request):
return Response({"status": "ok"}, status=status.HTTP_200_OK)
class ListCreateAtollView(StaffEditorPermissionMixin, generics.ListCreateAPIView):
class CreateAtollView(StaffEditorPermissionMixin, generics.CreateAPIView):
serializer_class = AtollSerializer
queryset = Atoll.objects.all()
@ -242,6 +253,13 @@ class ListCreateAtollView(StaffEditorPermissionMixin, generics.ListCreateAPIView
return super().create(request, *args, **kwargs)
class ListAtollView(generics.ListAPIView):
permission_classes = (permissions.AllowAny,)
serializer_class = AtollSerializer
queryset = Atoll.objects.all()
throttle_classes = () # override throttling
class RetrieveUpdateDestroyAtollView(
StaffEditorPermissionMixin, generics.RetrieveUpdateDestroyAPIView
):

View File

@ -219,7 +219,7 @@ def send_sms_with_callback_token(user, mobile_token, **kwargs):
"message": base_string % mobile_token.key,
"check_delivery": False,
}
print(mobile_token.key)
response = requests.post(api_url, headers=headers, data=json.dumps(data))
if response.status_code == 200:
return True

View File

@ -1,38 +0,0 @@
services:
api:
build:
context: .
dockerfile: Dockerfile.prod
restart: always
command: gunicorn apibase.wsgi:application --bind 0.0.0.0:5000 --workers=2
volumes:
- /home/<username>/docker/council-api/staticfiles:/home/app/api/staticfiles
ports:
- 5000:5000
env_file:
- ./.env
depends_on:
- db
- redis
db:
image: postgres:15
restart: always
volumes:
- ./postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DATABASE}
ports:
- 5232:5432
redis:
image: "redis:alpine"
restart: always
expose:
- "6379"

View File

@ -1,11 +1,14 @@
services:
api:
build: .
command: python manage.py runserver 0.0.0.0:8000
build:
context: .
dockerfile: Dockerfile.prod
restart: always
command: gunicorn apibase.wsgi:application --bind 0.0.0.0:5000 --workers=2
volumes:
- ./:/usr/src/app/
- /home/<username>/docker/council-api/staticfiles:/home/app/api/staticfiles
ports:
- 8000:8000
- 5000:5000
env_file:
- ./.env
depends_on:
@ -14,17 +17,22 @@ services:
db:
image: postgres:15
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data/
- ./postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DATABASE}
ports:
- 5232:5432
redis:
image: "redis:alpine"
restart: always
expose:
- "6379"
volumes:
postgres_data: