diff --git a/Dockerfile b/Dockerfile index e591831..84d3204 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] \ No newline at end of file +# 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"] diff --git a/Dockerfile.prod b/Dockerfile.prod deleted file mode 100644 index 84d3204..0000000 --- a/Dockerfile.prod +++ /dev/null @@ -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"] diff --git a/api/filters.py b/api/filters.py index 95bbdf7..b8cac86 100644 --- a/api/filters.py +++ b/api/filters.py @@ -13,4 +13,8 @@ class UserFilter(django_filters.FilterSet): "username", "last_name", "first_name", + "email", + "is_active", + "id_card", + "mobile", ] diff --git a/api/serializers.py b/api/serializers.py index e8f5fa0..1134c0e 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -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 diff --git a/api/urls.py b/api/urls.py index 8e93185..24389da 100644 --- a/api/urls.py +++ b/api/urls.py @@ -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//", 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//", RetrieveUpdateDestroyAtollView.as_view(), diff --git a/api/views.py b/api/views.py index 71c9a32..a2c5dc7 100644 --- a/api/views.py +++ b/api/views.py @@ -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 ): diff --git a/djangopasswordlessknox/utils.py b/djangopasswordlessknox/utils.py index df073a4..ea01f06 100644 --- a/djangopasswordlessknox/utils.py +++ b/djangopasswordlessknox/utils.py @@ -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 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 3855995..0000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -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//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" - - diff --git a/docker-compose.yml b/docker-compose.yml index 322e4b7..3855995 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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//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: + +