diff --git a/ServicesApi/Builds/TestApi/Dockerfile b/ServicesApi/Builds/TestApi/Dockerfile new file mode 100644 index 0000000..6689bc2 --- /dev/null +++ b/ServicesApi/Builds/TestApi/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.12-slim + +WORKDIR / + +# Install system dependencies and Poetry +RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry + +# Copy Poetry configuration +COPY /pyproject.toml ./pyproject.toml + +# Configure Poetry and install dependencies with optimizations +RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi --no-root --only main && pip cache purge && rm -rf ~/.cache/pypoetry + +# Copy application code +COPY /ServicesApi/Initializer /Initializer +COPY /ServicesApi/Controllers /Controllers +COPY /ServicesApi/Validations /Validations +COPY /ServicesApi/Schemas /Schemas +COPY /ServicesApi/Extensions /Extensions + +COPY /ServicesApi/Builds/TestApi/endpoints /endpoints +COPY /ServicesApi/Builds/TestApi/events /events +# COPY /api_services/api_builds/test_api/validations /api_initializer/validations +# COPY /api_services/api_builds/test_api/index.py /api_initializer/index.py + +# Set Python path to include app directory +ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 + +# Run the application using the configured uvicorn server +CMD ["poetry", "run", "python", "/Initializer/app.py"] diff --git a/ServicesApi/Builds/TestApi/endpoints/routes.py b/ServicesApi/Builds/TestApi/endpoints/routes.py new file mode 100644 index 0000000..4293187 --- /dev/null +++ b/ServicesApi/Builds/TestApi/endpoints/routes.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter +from .tester.router import tester_endpoint_route + + +def get_routes() -> list[APIRouter]: + return [tester_endpoint_route] + + +def get_safe_endpoint_urls() -> list[tuple[str, str]]: + return [ + ("/", "GET"), + ("/docs", "GET"), + ("/redoc", "GET"), + ("/openapi.json", "GET"), + ("/metrics", "GET"), + ("/tester/list", "POST"), + ] + \ No newline at end of file diff --git a/ServicesApi/Builds/TestApi/endpoints/tester/router.py b/ServicesApi/Builds/TestApi/endpoints/tester/router.py new file mode 100644 index 0000000..8b275b9 --- /dev/null +++ b/ServicesApi/Builds/TestApi/endpoints/tester/router.py @@ -0,0 +1,37 @@ +import datetime + +from typing import Any +from fastapi import APIRouter, Depends +from pydantic import BaseModel +from Validations.response import PaginateOnly, Pagination, PaginationResult, EndpointResponse +from Validations.defaults.validations import CommonHeaders +from Schemas import AccountRecords + + +tester_endpoint_route = APIRouter(prefix="/tester", tags=["Tester Cluster"]) + + +class TestList(BaseModel): + uu_id: str + bank_date: datetime.datetime + currency_value: float + process_name: str + + +tester_list = "TestList" +@tester_endpoint_route.post( + path="/list", + description="List all tester endpoint", + operation_id="4c38fab8-9b66-41cd-b87a-41175c9eea48", +) +def tester_list_route( + list_options: PaginateOnly, + headers: CommonHeaders = Depends(CommonHeaders.as_dependency), +): + with AccountRecords.new_session() as db_session: + AccountRecords.set_session(db_session) + tester_list = AccountRecords.query.filter(AccountRecords.currency_value > 0) + pagination = Pagination(data=tester_list, base_query=AccountRecords.query.filter()) + pagination.change(**list_options.model_dump()) + pagination_result = PaginationResult(data=tester_list, pagination=pagination, response_model=TestList) + return EndpointResponse(message="MSG0003-LIST", pagination_result=pagination_result).response diff --git a/ServicesApi/Builds/TestApi/events/__init__.py b/ServicesApi/Builds/TestApi/events/__init__.py new file mode 100644 index 0000000..5b694f7 --- /dev/null +++ b/ServicesApi/Builds/TestApi/events/__init__.py @@ -0,0 +1,3 @@ + + +__all__ = [] diff --git a/ServicesApi/Controllers/Postgres/engine.py b/ServicesApi/Controllers/Postgres/engine.py index 3c294e8..4d76854 100644 --- a/ServicesApi/Controllers/Postgres/engine.py +++ b/ServicesApi/Controllers/Postgres/engine.py @@ -1,11 +1,12 @@ from contextlib import contextmanager from functools import lru_cache from typing import Generator -from api_controllers.postgres.config import postgres_configs from sqlalchemy import create_engine from sqlalchemy.orm import declarative_base, sessionmaker, scoped_session, Session +from Controllers.Postgres.config import postgres_configs + # Configure the database engine with proper pooling engine = create_engine( diff --git a/ServicesApi/Controllers/Postgres/mixin.py b/ServicesApi/Controllers/Postgres/mixin.py index 7d87693..8020a59 100644 --- a/ServicesApi/Controllers/Postgres/mixin.py +++ b/ServicesApi/Controllers/Postgres/mixin.py @@ -13,7 +13,7 @@ from sqlalchemy_mixins.repr import ReprMixin from sqlalchemy_mixins.smartquery import SmartQueryMixin from sqlalchemy_mixins.activerecord import ActiveRecordMixin -from api_controllers.postgres.engine import get_db, Base +from Controllers.Postgres.engine import get_db, Base T = TypeVar("CrudMixin", bound="CrudMixin") @@ -72,7 +72,7 @@ class BasicMixin(Base, ActiveRecordMixin, SerializeMixin, ReprMixin, SmartQueryM return True, str(arrow.get(str(val)).format("YYYY-MM-DD HH:mm:ss")) elif isinstance(val, bool): return True, bool(val) - elif isinstance(val, (float, Decimal)): + elif isinstance(val, float) or isinstance(val, Decimal): return True, round(float(val), 3) elif isinstance(val, int): return True, int(val) @@ -81,7 +81,6 @@ class BasicMixin(Base, ActiveRecordMixin, SerializeMixin, ReprMixin, SmartQueryM elif val is None: return True, None return False, None - except Exception as e: err = e return False, None @@ -160,7 +159,6 @@ class BasicMixin(Base, ActiveRecordMixin, SerializeMixin, ReprMixin, SmartQueryM return {} - class CrudMixin(BasicMixin): """ Base mixin providing CRUD operations and common fields for PostgreSQL models. @@ -272,4 +270,3 @@ class CrudCollection(CrudMixin): is_email_send: Mapped[bool] = mapped_column( Boolean, server_default="0", comment="Email sent flag" ) - diff --git a/ServicesApi/Extensions/Middlewares/__init__.py b/ServicesApi/Extensions/Middlewares/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ServicesApi/Extensions/Middlewares/token_middleware.py b/ServicesApi/Extensions/Middlewares/token_middleware.py new file mode 100644 index 0000000..950dc6a --- /dev/null +++ b/ServicesApi/Extensions/Middlewares/token_middleware.py @@ -0,0 +1,19 @@ +from fastapi import Request, status +from fastapi.responses import JSONResponse +from config import api_config + +from endpoints.routes import get_safe_endpoint_urls + + +async def token_middleware(request: Request, call_next): + base_url = request.url.path + safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()] + if base_url in safe_endpoints: + return await call_next(request) + + token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) + if not token: + return JSONResponse(content={"error": "EYS_0002"}, status_code=status.HTTP_401_UNAUTHORIZED) + + response = await call_next(request) + return response diff --git a/ServicesApi/Extensions/Middlewares/token_provider.py b/ServicesApi/Extensions/Middlewares/token_provider.py new file mode 100644 index 0000000..5e16c08 --- /dev/null +++ b/ServicesApi/Extensions/Middlewares/token_provider.py @@ -0,0 +1,96 @@ +import enum + +from typing import Optional, Union, Dict, Any, List +from pydantic import BaseModel + +from api_controllers.redis.database import RedisActions +from api_validations.token.validations import ( + TokenDictType, + OccupantTokenObject, + EmployeeTokenObject, + UserType, +) + +class TokenProvider: + + AUTH_TOKEN: str = "AUTH_TOKEN" + + @classmethod + def convert_redis_object_to_token(cls, redis_object: Dict[str, Any]) -> TokenDictType: + """ + Process Redis object and return appropriate token object. + """ + if redis_object.get("user_type") == UserType.employee.value: + return EmployeeTokenObject(**redis_object) + elif redis_object.get("user_type") == UserType.occupant.value: + return OccupantTokenObject(**redis_object) + raise ValueError("Invalid user type") + + + @classmethod + def get_login_token_from_redis( + cls, token: Optional[str] = None, user_uu_id: Optional[str] = None + ) -> Union[TokenDictType, List[TokenDictType]]: + """ + Retrieve token object from Redis using token and user_uu_id + """ + token_to_use, user_uu_id_to_use = token or "*", user_uu_id or "*" + list_of_token_dict, auth_key_list = [], [cls.AUTH_TOKEN, token_to_use, user_uu_id_to_use] + if token: + result = RedisActions.get_json(list_keys=auth_key_list, limit=1) + if first_record := result.first: + return cls.convert_redis_object_to_token(first_record) + elif user_uu_id: + result = RedisActions.get_json(list_keys=auth_key_list) + if all_records := result.all: + for all_record in all_records: + list_of_token_dict.append(cls.convert_redis_object_to_token(all_record)) + return list_of_token_dict + raise ValueError("Token not found in Redis. Please check the token or user_uu_id.") + + @classmethod + def get_dict_from_redis( + cls, token: Optional[str] = None, user_uu_id: Optional[str] = None + ) -> Union[TokenDictType, List[TokenDictType]]: + """ + Retrieve token object from Redis using token and user_uu_id + """ + token_to_use, user_uu_id_to_use = token or "*", user_uu_id or "*" + list_of_token_dict, auth_key_list = [], [cls.AUTH_TOKEN, token_to_use, user_uu_id_to_use, "*"] + if token: + result = RedisActions.get_json(list_keys=auth_key_list, limit=1) + if first_record := result.first: + return cls.convert_redis_object_to_token(first_record) + elif user_uu_id: + result = RedisActions.get_json(list_keys=auth_key_list) + if all_records := result.all: + for all_record in all_records: + list_of_token_dict.append(cls.convert_redis_object_to_token(all_record)) + return list_of_token_dict + raise ValueError("Token not found in Redis. Please check the token or user_uu_id.") + + @classmethod + def retrieve_application_codes(cls, page_url: str, token: TokenDictType): + """ + Retrieve application code from the token object or list of token objects. + """ + if isinstance(token, EmployeeTokenObject): + if application_codes := token.selected_company.reachable_app_codes.get(page_url, None): + return application_codes + elif isinstance(token, OccupantTokenObject): + if application_codes := token.selected_occupant.reachable_app_codes.get(page_url, None): + return application_codes + raise ValueError("Invalid token type or no application code found.") + + @classmethod + def retrieve_event_codes(cls, endpoint_code: str, token: TokenDictType) -> str: + """ + Retrieve event code from the token object or list of token objects. + """ + if isinstance(token, EmployeeTokenObject): + if event_codes := token.selected_company.reachable_event_codes.get(endpoint_code, None): + return event_codes + elif isinstance(token, OccupantTokenObject): + if event_codes := token.selected_occupant.reachable_event_codes.get(endpoint_code, None): + return event_codes + raise ValueError("Invalid token type or no event code found.") diff --git a/ServicesApi/Extends/Token/config.py b/ServicesApi/Extensions/Token/config.py similarity index 100% rename from ServicesApi/Extends/Token/config.py rename to ServicesApi/Extensions/Token/config.py diff --git a/ServicesApi/Extends/Token/password_module.py b/ServicesApi/Extensions/Token/password_module.py similarity index 100% rename from ServicesApi/Extends/Token/password_module.py rename to ServicesApi/Extensions/Token/password_module.py diff --git a/ServicesApi/Initializer/app.py b/ServicesApi/Initializer/app.py index 5555a91..eebcd39 100644 --- a/ServicesApi/Initializer/app.py +++ b/ServicesApi/Initializer/app.py @@ -1,7 +1,7 @@ import uvicorn -from api_initializer.config import api_config -from api_initializer.create_app import create_app +from config import api_config +from create_app import create_app # from prometheus_fastapi_instrumentator import Instrumentator diff --git a/ServicesApi/Initializer/create_app.py b/ServicesApi/Initializer/create_app.py index 87ba23d..2dda016 100644 --- a/ServicesApi/Initializer/create_app.py +++ b/ServicesApi/Initializer/create_app.py @@ -1,3 +1,5 @@ +import events + from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse @@ -6,9 +8,8 @@ from config import api_config from open_api_creator import create_openapi_schema from create_route import RouteRegisterController -from api_middlewares.token_middleware import token_middleware +from Extensions.Middlewares.token_middleware import token_middleware from endpoints.routes import get_routes -import events cluster_is_set = False diff --git a/ServicesApi/Initializer/create_route.py b/ServicesApi/Initializer/create_route.py index 438a77e..7962128 100644 --- a/ServicesApi/Initializer/create_route.py +++ b/ServicesApi/Initializer/create_route.py @@ -10,7 +10,7 @@ class RouteRegisterController: @staticmethod def add_router_with_event_to_database(router: APIRouter): - from schemas import EndpointRestriction + from Schemas import EndpointRestriction with EndpointRestriction.new_session() as db_session: EndpointRestriction.set_session(db_session) diff --git a/ServicesApi/Schemas/base_imports.py b/ServicesApi/Schemas/base_imports.py index 2797581..f6978e0 100644 --- a/ServicesApi/Schemas/base_imports.py +++ b/ServicesApi/Schemas/base_imports.py @@ -1,4 +1,4 @@ -from ApiControllers.postgres.mixin import CrudCollection +from Controllers.Postgres.mixin import CrudCollection from sqlalchemy.orm import mapped_column, Mapped, relationship from sqlalchemy import ( String, diff --git a/ServicesApi/Validations/response/api.py b/ServicesApi/Validations/response/api.py index 2b89c00..b71f6ce 100644 --- a/ServicesApi/Validations/response/api.py +++ b/ServicesApi/Validations/response/api.py @@ -31,8 +31,8 @@ class EndpointResponse(BaseModel): return { "completed": self.completed, "message": self.message, - "data": result_data, "pagination": pagination_dict, + "data": result_data, } model_config = { diff --git a/ServicesApi/Validations/response/result.py b/ServicesApi/Validations/response/result.py index 418c8db..7e6165a 100644 --- a/ServicesApi/Validations/response/result.py +++ b/ServicesApi/Validations/response/result.py @@ -26,7 +26,8 @@ class Pagination: MIN_SIZE = default_paginate_config.MIN_SIZE MAX_SIZE = default_paginate_config.MAX_SIZE - def __init__(self, data: PostgresResponse): + def __init__(self, data: Query, base_query: Query): + self.base_query = base_query self.query = data self.size: int = self.DEFAULT_SIZE self.page: int = 1 @@ -60,7 +61,7 @@ class Pagination: """Update page counts and validate current page.""" if self.query: self.total_count = self.query.count() - self.all_count = self.query.count() + self.all_count = self.base_query.count() self.size = ( self.size @@ -151,24 +152,14 @@ class PaginationResult: Ordered query object. """ if not len(self.order_by) == len(self.pagination.orderType): - raise ValueError( - "Order by fields and order types must have the same length." - ) + raise ValueError("Order by fields and order types must have the same length.") order_criteria = zip(self.order_by, self.pagination.orderType) for field, direction in order_criteria: if hasattr(self._query.column_descriptions[0]["entity"], field): if direction.lower().startswith("d"): - self._query = self._query.order_by( - desc( - getattr(self._query.column_descriptions[0]["entity"], field) - ) - ) + self._query = self._query.order_by(desc(getattr(self._query.column_descriptions[0]["entity"], field))) else: - self._query = self._query.order_by( - asc( - getattr(self._query.column_descriptions[0]["entity"], field) - ) - ) + self._query = self._query.order_by(asc(getattr(self._query.column_descriptions[0]["entity"], field))) return self._query @property diff --git a/ServicesWeb/customer/src/app/api/a.txt b/ServicesWeb/customer/src/app/api/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/ServicesWeb/customer/src/app/api/test/route.ts b/ServicesWeb/customer/src/app/api/test/route.ts new file mode 100644 index 0000000..9d064a2 --- /dev/null +++ b/ServicesWeb/customer/src/app/api/test/route.ts @@ -0,0 +1,26 @@ +import { NextResponse } from "next/server"; +import { fetchTest } from "@/fetchers/custom/test/fetch"; + +export async function GET() { + try { + return NextResponse.json({ status: 200, data: { message: "Test" } }); + } catch (error) { + return NextResponse.json({ status: 500, message: "No data is found" }); + } +} + +export async function POST(request: Request) { + const body = await request.json(); + try { + const data = await fetchTest({ + page: body.page, + size: body.size, + orderField: body.orderField, + orderType: body.orderType, + query: body.query, + }); + return NextResponse.json({ status: 200, data }); + } catch (error) { + return NextResponse.json({ status: 500, message: "No data is found" }); + } +} diff --git a/ServicesWeb/customer/src/app/test/page.tsx b/ServicesWeb/customer/src/app/test/page.tsx new file mode 100644 index 0000000..d8b7e1c --- /dev/null +++ b/ServicesWeb/customer/src/app/test/page.tsx @@ -0,0 +1,169 @@ +'use client'; +import React, { useState } from "react"; +import { apiPostFetcher, apiGetFetcher } from "@/lib/fetcher" +import { API_BASE_URL } from "@/config/config" +import { Button } from "@/components/mutual/ui/button" +import { Input } from "@/components/mutual/ui/input"; +import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/mutual/ui/form" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import * as z from "zod" + +// Define the form schema with Zod +const formSchema = z.object({ + page: z.number().min(1, "Page must be at least 1"), + size: z.number().min(1, "Size must be at least 1"), + orderField: z.array(z.string()), + orderType: z.array(z.string()), + query: z.any() +}); + +// Define the type for our form values +type FormValues = z.infer; + +export default function TestPage() { + const [testPostResult, setTestPostResult] = useState({}); + const [testGetResult, setTestGetResult] = useState({}); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + page: 1, + size: 10, + orderField: [], + orderType: [], + query: {} + } + }); + + const testPost = async (values: FormValues) => { + const result = await apiPostFetcher({ + url: `${API_BASE_URL}/test`, + body: values, + isNoCache: true + }) + setTestPostResult(result); + } + + const testGet = async () => { + const result = await apiGetFetcher({ + url: `${API_BASE_URL}/test`, + isNoCache: true + }) + setTestGetResult(result); + } + + return ( +
+
+ + ( + + Page + + field.onChange(Number(e.target.value))} /> + + + + )} + /> + ( + + Size + + field.onChange(Number(e.target.value))} /> + + + + )} + /> + ( + + Order Field + + field.onChange(e.target.value.split(","))} + /> + + Comma-separated list of fields + + + )} + /> + ( + + Order Type + + field.onChange(e.target.value.split(","))} + /> + + Comma-separated list of types (asc/desc) + + + )} + /> + ( + + Query + + { + try { + field.onChange(JSON.parse(e.target.value)) + } catch (error) { + // Handle JSON parse error + } + }} + /> + + JSON format + + + )} + /> + + + + + + {/*
{JSON.stringify(testPostResult?.data?.data || [])}
*/} +
+ {(testPostResult?.data?.data?.map((item: any) => ( +
+ UUID:{item.uu_id} + Name:{item.process_name} + Bank Date:{item.bank_date} + Currency Value:{item.currency_value} +
+ )) + )} +
+
+ ); +} diff --git a/ServicesWeb/customer/src/components/ui/accordion.tsx b/ServicesWeb/customer/src/components/mutual/ui/accordion.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/accordion.tsx rename to ServicesWeb/customer/src/components/mutual/ui/accordion.tsx diff --git a/ServicesWeb/customer/src/components/ui/alert-dialog.tsx b/ServicesWeb/customer/src/components/mutual/ui/alert-dialog.tsx similarity index 98% rename from ServicesWeb/customer/src/components/ui/alert-dialog.tsx rename to ServicesWeb/customer/src/components/mutual/ui/alert-dialog.tsx index 0863e40..2dd7ac1 100644 --- a/ServicesWeb/customer/src/components/ui/alert-dialog.tsx +++ b/ServicesWeb/customer/src/components/mutual/ui/alert-dialog.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +import { buttonVariants } from "@/components/mutual/ui/button" function AlertDialog({ ...props diff --git a/ServicesWeb/customer/src/components/ui/alert.tsx b/ServicesWeb/customer/src/components/mutual/ui/alert.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/alert.tsx rename to ServicesWeb/customer/src/components/mutual/ui/alert.tsx diff --git a/ServicesWeb/customer/src/components/ui/aspect-ratio.tsx b/ServicesWeb/customer/src/components/mutual/ui/aspect-ratio.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/aspect-ratio.tsx rename to ServicesWeb/customer/src/components/mutual/ui/aspect-ratio.tsx diff --git a/ServicesWeb/customer/src/components/ui/avatar.tsx b/ServicesWeb/customer/src/components/mutual/ui/avatar.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/avatar.tsx rename to ServicesWeb/customer/src/components/mutual/ui/avatar.tsx diff --git a/ServicesWeb/customer/src/components/ui/badge.tsx b/ServicesWeb/customer/src/components/mutual/ui/badge.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/badge.tsx rename to ServicesWeb/customer/src/components/mutual/ui/badge.tsx diff --git a/ServicesWeb/customer/src/components/ui/button.tsx b/ServicesWeb/customer/src/components/mutual/ui/button.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/button.tsx rename to ServicesWeb/customer/src/components/mutual/ui/button.tsx diff --git a/ServicesWeb/management/src/components/ui/calendar.tsx b/ServicesWeb/customer/src/components/mutual/ui/calendar.tsx similarity index 97% rename from ServicesWeb/management/src/components/ui/calendar.tsx rename to ServicesWeb/customer/src/components/mutual/ui/calendar.tsx index b8df044..541868a 100644 --- a/ServicesWeb/management/src/components/ui/calendar.tsx +++ b/ServicesWeb/customer/src/components/mutual/ui/calendar.tsx @@ -5,7 +5,7 @@ import { ChevronLeft, ChevronRight } from "lucide-react" import { DayPicker } from "react-day-picker" import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +import { buttonVariants } from "@/components/mutual/ui/button" function Calendar({ className, diff --git a/ServicesWeb/customer/src/components/ui/card.tsx b/ServicesWeb/customer/src/components/mutual/ui/card.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/card.tsx rename to ServicesWeb/customer/src/components/mutual/ui/card.tsx diff --git a/ServicesWeb/customer/src/components/ui/checkbox.tsx b/ServicesWeb/customer/src/components/mutual/ui/checkbox.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/checkbox.tsx rename to ServicesWeb/customer/src/components/mutual/ui/checkbox.tsx diff --git a/ServicesWeb/customer/src/components/ui/collapsible.tsx b/ServicesWeb/customer/src/components/mutual/ui/collapsible.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/collapsible.tsx rename to ServicesWeb/customer/src/components/mutual/ui/collapsible.tsx diff --git a/ServicesWeb/customer/src/components/ui/command.tsx b/ServicesWeb/customer/src/components/mutual/ui/command.tsx similarity index 99% rename from ServicesWeb/customer/src/components/ui/command.tsx rename to ServicesWeb/customer/src/components/mutual/ui/command.tsx index 4ca5349..3467b11 100644 --- a/ServicesWeb/customer/src/components/ui/command.tsx +++ b/ServicesWeb/customer/src/components/mutual/ui/command.tsx @@ -11,7 +11,7 @@ import { DialogDescription, DialogHeader, DialogTitle, -} from "@/components/ui/dialog" +} from "@/components/mutual/ui/dialog" function Command({ className, diff --git a/ServicesWeb/customer/src/components/ui/context-menu.tsx b/ServicesWeb/customer/src/components/mutual/ui/context-menu.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/context-menu.tsx rename to ServicesWeb/customer/src/components/mutual/ui/context-menu.tsx diff --git a/ServicesWeb/customer/src/components/ui/dialog.tsx b/ServicesWeb/customer/src/components/mutual/ui/dialog.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/dialog.tsx rename to ServicesWeb/customer/src/components/mutual/ui/dialog.tsx diff --git a/ServicesWeb/customer/src/components/ui/dropdown-menu.tsx b/ServicesWeb/customer/src/components/mutual/ui/dropdown-menu.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/dropdown-menu.tsx rename to ServicesWeb/customer/src/components/mutual/ui/dropdown-menu.tsx diff --git a/ServicesWeb/management/src/components/ui/form.tsx b/ServicesWeb/customer/src/components/mutual/ui/form.tsx similarity index 98% rename from ServicesWeb/management/src/components/ui/form.tsx rename to ServicesWeb/customer/src/components/mutual/ui/form.tsx index 524b986..8c65a07 100644 --- a/ServicesWeb/management/src/components/ui/form.tsx +++ b/ServicesWeb/customer/src/components/mutual/ui/form.tsx @@ -14,7 +14,7 @@ import { } from "react-hook-form" import { cn } from "@/lib/utils" -import { Label } from "@/components/ui/label" +import { Label } from "@/components/mutual/ui/label" const Form = FormProvider diff --git a/ServicesWeb/customer/src/components/ui/hover-card.tsx b/ServicesWeb/customer/src/components/mutual/ui/hover-card.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/hover-card.tsx rename to ServicesWeb/customer/src/components/mutual/ui/hover-card.tsx diff --git a/ServicesWeb/customer/src/components/ui/input.tsx b/ServicesWeb/customer/src/components/mutual/ui/input.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/input.tsx rename to ServicesWeb/customer/src/components/mutual/ui/input.tsx diff --git a/ServicesWeb/customer/src/components/ui/label.tsx b/ServicesWeb/customer/src/components/mutual/ui/label.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/label.tsx rename to ServicesWeb/customer/src/components/mutual/ui/label.tsx diff --git a/ServicesWeb/customer/src/components/ui/menubar.tsx b/ServicesWeb/customer/src/components/mutual/ui/menubar.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/menubar.tsx rename to ServicesWeb/customer/src/components/mutual/ui/menubar.tsx diff --git a/ServicesWeb/customer/src/components/ui/navigation-menu.tsx b/ServicesWeb/customer/src/components/mutual/ui/navigation-menu.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/navigation-menu.tsx rename to ServicesWeb/customer/src/components/mutual/ui/navigation-menu.tsx diff --git a/ServicesWeb/customer/src/components/ui/popover.tsx b/ServicesWeb/customer/src/components/mutual/ui/popover.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/popover.tsx rename to ServicesWeb/customer/src/components/mutual/ui/popover.tsx diff --git a/ServicesWeb/customer/src/components/ui/progress.tsx b/ServicesWeb/customer/src/components/mutual/ui/progress.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/progress.tsx rename to ServicesWeb/customer/src/components/mutual/ui/progress.tsx diff --git a/ServicesWeb/customer/src/components/ui/radio-group.tsx b/ServicesWeb/customer/src/components/mutual/ui/radio-group.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/radio-group.tsx rename to ServicesWeb/customer/src/components/mutual/ui/radio-group.tsx diff --git a/ServicesWeb/customer/src/components/ui/scroll-area.tsx b/ServicesWeb/customer/src/components/mutual/ui/scroll-area.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/scroll-area.tsx rename to ServicesWeb/customer/src/components/mutual/ui/scroll-area.tsx diff --git a/ServicesWeb/customer/src/components/ui/select.tsx b/ServicesWeb/customer/src/components/mutual/ui/select.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/select.tsx rename to ServicesWeb/customer/src/components/mutual/ui/select.tsx diff --git a/ServicesWeb/customer/src/components/ui/separator.tsx b/ServicesWeb/customer/src/components/mutual/ui/separator.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/separator.tsx rename to ServicesWeb/customer/src/components/mutual/ui/separator.tsx diff --git a/ServicesWeb/customer/src/components/ui/sheet.tsx b/ServicesWeb/customer/src/components/mutual/ui/sheet.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/sheet.tsx rename to ServicesWeb/customer/src/components/mutual/ui/sheet.tsx diff --git a/ServicesWeb/customer/src/components/ui/skeleton.tsx b/ServicesWeb/customer/src/components/mutual/ui/skeleton.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/skeleton.tsx rename to ServicesWeb/customer/src/components/mutual/ui/skeleton.tsx diff --git a/ServicesWeb/customer/src/components/ui/slider.tsx b/ServicesWeb/customer/src/components/mutual/ui/slider.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/slider.tsx rename to ServicesWeb/customer/src/components/mutual/ui/slider.tsx diff --git a/ServicesWeb/customer/src/components/ui/sonner.tsx b/ServicesWeb/customer/src/components/mutual/ui/sonner.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/sonner.tsx rename to ServicesWeb/customer/src/components/mutual/ui/sonner.tsx diff --git a/ServicesWeb/customer/src/components/ui/switch.tsx b/ServicesWeb/customer/src/components/mutual/ui/switch.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/switch.tsx rename to ServicesWeb/customer/src/components/mutual/ui/switch.tsx diff --git a/ServicesWeb/customer/src/components/ui/table.tsx b/ServicesWeb/customer/src/components/mutual/ui/table.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/table.tsx rename to ServicesWeb/customer/src/components/mutual/ui/table.tsx diff --git a/ServicesWeb/customer/src/components/ui/tabs.tsx b/ServicesWeb/customer/src/components/mutual/ui/tabs.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/tabs.tsx rename to ServicesWeb/customer/src/components/mutual/ui/tabs.tsx diff --git a/ServicesWeb/customer/src/components/ui/textarea.tsx b/ServicesWeb/customer/src/components/mutual/ui/textarea.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/textarea.tsx rename to ServicesWeb/customer/src/components/mutual/ui/textarea.tsx diff --git a/ServicesWeb/customer/src/components/ui/toggle.tsx b/ServicesWeb/customer/src/components/mutual/ui/toggle.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/toggle.tsx rename to ServicesWeb/customer/src/components/mutual/ui/toggle.tsx diff --git a/ServicesWeb/customer/src/components/ui/tooltip.tsx b/ServicesWeb/customer/src/components/mutual/ui/tooltip.tsx similarity index 100% rename from ServicesWeb/customer/src/components/ui/tooltip.tsx rename to ServicesWeb/customer/src/components/mutual/ui/tooltip.tsx diff --git a/ServicesWeb/customer/src/fetchers/custom/test/fetch.tsx b/ServicesWeb/customer/src/fetchers/custom/test/fetch.tsx new file mode 100644 index 0000000..aa73ef2 --- /dev/null +++ b/ServicesWeb/customer/src/fetchers/custom/test/fetch.tsx @@ -0,0 +1,29 @@ +"use server"; +import { fetchData } from "@/fetchers/fecther"; +import { urlTesterList } from "@/fetchers/index"; + +export const fetchTest = async ({ + page, + size, + orderField, + orderType, + query, +}: { + page?: number; + size?: number; + orderField?: string[]; + orderType?: string[]; + query?: any; +}) => { + try { + const response = await fetchData(urlTesterList, { method: "POST", cache: false, payload: { page, size, orderField, orderType, query } }); + return response.data; + } catch (error) { + console.error(error); + return { + completed: false, + data: null, + message: "No data is found", + }; + } +}; diff --git a/ServicesWeb/customer/src/fetchers/index.ts b/ServicesWeb/customer/src/fetchers/index.ts index eb695db..96a0044 100644 --- a/ServicesWeb/customer/src/fetchers/index.ts +++ b/ServicesWeb/customer/src/fetchers/index.ts @@ -1,36 +1,36 @@ import { formatServiceUrl } from "./utils"; const baseUrlAuth = formatServiceUrl( - process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "auth_service:8001" + process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "localhost:8001" ); const baseUrlRestriction = formatServiceUrl( - process.env.NEXT_PUBLIC_RESTRICTION_SERVICE_URL || "restriction_service:8002" + process.env.NEXT_PUBLIC_RESTRICTION_SERVICE_URL || "localhost:8002" ); const baseUrlApplication = formatServiceUrl( - process.env.NEXT_PUBLIC_MANAGEMENT_SERVICE_URL || "management_service:8003" + process.env.NEXT_PUBLIC_MANAGEMENT_SERVICE_URL || "localhost:8003" ); const baseUrlAccount = formatServiceUrl( - process.env.NEXT_PUBLIC_ACCOUNT_SERVICE_URL || "account_service:8004" + process.env.NEXT_PUBLIC_ACCOUNT_SERVICE_URL || "localhost:8004" ); const baseUrlBuilding = formatServiceUrl( - process.env.NEXT_PUBLIC_BUILDING_SERVICE_URL || "building_service:8006" + process.env.NEXT_PUBLIC_BUILDING_SERVICE_URL || "localhost:8006" ); const baseUrlPeople = formatServiceUrl( - process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "validation_service:8009" + process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "localhost:8009" +); +const baseUrlTester = formatServiceUrl( + process.env.NEXT_PUBLIC_TESTER_SERVICE_URL || "localhost:8005" ); - const urlCheckToken = `${baseUrlAuth}/authentication/token/check`; const urlPageValid = `${baseUrlRestriction}/restrictions/page/valid`; const urlSiteUrls = `${baseUrlRestriction}/restrictions/sites/list`; +const urlTesterList = `${baseUrlTester}/tester/list`; + export { - baseUrlAuth, - baseUrlPeople, - baseUrlApplication, - baseUrlAccount, - baseUrlBuilding, - baseUrlRestriction, urlCheckToken, urlPageValid, urlSiteUrls, + // For test use only + urlTesterList, }; diff --git a/ServicesWeb/customer/src/fetchers/mutual/cookies/token.tsx b/ServicesWeb/customer/src/fetchers/mutual/cookies/token.tsx index 58f55f7..af2e086 100644 --- a/ServicesWeb/customer/src/fetchers/mutual/cookies/token.tsx +++ b/ServicesWeb/customer/src/fetchers/mutual/cookies/token.tsx @@ -2,7 +2,7 @@ import { cookies } from "next/headers"; import { fetchDataWithToken } from "@/fetchers/fecther"; import { urlCheckToken, urlPageValid, urlSiteUrls } from "@/fetchers/index"; import { nextCrypto } from "@/fetchers/base"; -import { AuthError } from "@/validations/mutual/context/validations"; +import { AuthError } from "@/fetchers/types/context"; import { fetchResponseStatus } from "@/fetchers/utils"; async function checkAccessTokenIsValid() { diff --git a/ServicesWeb/customer/src/utils/apiOperations.ts b/ServicesWeb/customer/src/utils/apiOperations.ts new file mode 100644 index 0000000..01c55d7 --- /dev/null +++ b/ServicesWeb/customer/src/utils/apiOperations.ts @@ -0,0 +1,165 @@ +import { NextRequest } from "next/server"; +import { + successResponse, + errorResponse, + paginationResponse, + createResponse, + updateResponse, + deleteResponse, +} from "./responseHandlers"; +import { withErrorHandling, validateRequiredFields } from "./requestHandlers"; +import { + ApiHandler, + PaginationParams, + ListFunction, + CreateFunction, + UpdateFunction, + DeleteFunction, +} from "./types"; + +/** + * Generic list operation handler + * @param request NextRequest object + * @param body Request body + * @param listFunction The function to call to get the list data + */ +export async function handleListOperation( + request: NextRequest, + body: any, + listFunction: ListFunction +) { + const page = body.page || 1; + const size = body.size || 10; + const orderField = body.orderField || ["uu_id"]; + const orderType = body.orderType || ["asc"]; + const query = body.query || {}; + const response = await listFunction({ + page, + size, + orderField, + orderType, + query, + } as PaginationParams); + return paginationResponse(response.data, response.pagination); +} + +/** + * Generic create operation handler + * @param request NextRequest object + * @param body Request body + * @param createFunction The function to call to create the item + * @param requiredFields Array of required field names + */ +export async function handleCreateOperation( + body: any, + createFunction?: CreateFunction, + requiredFields: string[] = [] +) { + if (requiredFields.length > 0) { + const validation = validateRequiredFields(body, requiredFields); + if (!validation.valid) { + return errorResponse(validation.error as string, 400); + } + } + + if (createFunction) { + const result = await createFunction(body); + return createResponse(result); + } + + return createResponse({ + uuid: Math.floor(Math.random() * 1000), + ...body, + }); +} + +/** + * Generic update operation handler + * @param request NextRequest object + * @param body Request body + * @param updateFunction The function to call to update the item + */ +export async function handleUpdateOperation( + request: NextRequest, + body: any, + updateFunction?: UpdateFunction +) { + const uuid = request.nextUrl.searchParams.get("uuid"); + if (!uuid) { + return errorResponse("UUID not found", 400); + } + if (updateFunction) { + const result = await updateFunction(body, uuid); + return updateResponse(result); + } + return updateResponse(body); +} + +/** + * Generic delete operation handler + * @param request NextRequest object + * @param deleteFunction The function to call to delete the item + */ +export async function handleDeleteOperation( + request: NextRequest, + deleteFunction?: DeleteFunction +) { + const uuid = request.nextUrl.searchParams.get("uuid"); + if (!uuid) { + return errorResponse("UUID not found", 400); + } + + if (deleteFunction) { + await deleteFunction(uuid); + } + return deleteResponse(); +} + +/** + * Create a wrapped list handler with error handling + * @param listFunction The function to call to get the list data + */ +export function createListHandler(listFunction: ListFunction) { + return withErrorHandling((request: NextRequest, body: any) => + handleListOperation(request, body, listFunction) + ); +} + +/** + * Create a wrapped create handler with error handling + * @param createFunction The function to call to create the item + * @param requiredFields Array of required field names + */ +export function createCreateHandler( + createFunction?: CreateFunction, + requiredFields: string[] = [] +) { + // This handler only takes the body parameter, not the request + return withErrorHandling((body: any) => { + // Ensure we're only passing the actual body data to the create function + if (body && typeof body === 'object' && body.body) { + return handleCreateOperation(body.body, createFunction, requiredFields); + } + return handleCreateOperation(body, createFunction, requiredFields); + }); +} + +/** + * Create a wrapped update handler with error handling + * @param updateFunction The function to call to update the item + */ +export function createUpdateHandler(updateFunction?: UpdateFunction) { + return withErrorHandling((request: NextRequest, body: any) => + handleUpdateOperation(request, body, updateFunction) + ); +} + +/** + * Create a wrapped delete handler with error handling + * @param deleteFunction The function to call to delete the item + */ +export function createDeleteHandler(deleteFunction?: DeleteFunction) { + return withErrorHandling((request: NextRequest) => + handleDeleteOperation(request, deleteFunction) + ); +} diff --git a/ServicesWeb/customer/src/utils/index.ts b/ServicesWeb/customer/src/utils/index.ts new file mode 100644 index 0000000..e844833 --- /dev/null +++ b/ServicesWeb/customer/src/utils/index.ts @@ -0,0 +1,15 @@ +export { + successResponse, + errorResponse, + paginationResponse, + createResponse, + updateResponse, + deleteResponse, +} from "./responseHandlers"; +export { withErrorHandling, validateRequiredFields } from "./requestHandlers"; +export { + createListHandler, + createCreateHandler, + createUpdateHandler, + createDeleteHandler, +} from "./apiOperations"; diff --git a/ServicesWeb/customer/src/utils/requestHandlers.ts b/ServicesWeb/customer/src/utils/requestHandlers.ts new file mode 100644 index 0000000..ec8ecda --- /dev/null +++ b/ServicesWeb/customer/src/utils/requestHandlers.ts @@ -0,0 +1,70 @@ +import { NextRequest } from "next/server"; +import { errorResponse } from "./responseHandlers"; +import { ValidationResult, ApiHandler, ApiHandlerBodyOnly, ApiHandlerWithRequest } from "./types"; + +/** + * Safely parse JSON request body with error handling + * @param request NextRequest object + * @returns Parsed request body or null if parsing fails + */ +export async function parseRequestBody(request: NextRequest) { + try { + return await request.json(); + } catch (error) { + return null; + } +} + +/** + * Wrapper for API route handlers with built-in error handling + * @param handler The handler function to wrap + */ +export function withErrorHandling( + handler: ApiHandler +) { + return async (request: NextRequest) => { + try { + const body = await parseRequestBody(request); + + if (body === null) { + return errorResponse("Invalid request body", 400); + } + + // Check handler parameter count to determine if it needs request object + // If handler has only 1 parameter, it's likely a create operation that only needs body + if (handler.length === 1) { + // Cast to the appropriate handler type + return await (handler as ApiHandlerBodyOnly)(body); + } else { + // Otherwise pass both request and body (for list, update, delete operations) + return await (handler as ApiHandlerWithRequest)(request, body); + } + } catch (error: any) { + return errorResponse( + error.message || "Internal Server Error", + error.status || 500 + ); + } + }; +} + +/** + * Validate that required fields are present in the request body + * @param body Request body + * @param requiredFields Array of required field names + * @returns Object with validation result and error message if validation fails + */ +export function validateRequiredFields(body: any, requiredFields: string[]): ValidationResult { + const missingFields = requiredFields.filter(field => + body[field] === undefined || body[field] === null || body[field] === '' + ); + + if (missingFields.length > 0) { + return { + valid: false, + error: `Missing required fields: ${missingFields.join(', ')}` + }; + } + + return { valid: true }; +} diff --git a/ServicesWeb/customer/src/utils/responseHandlers.ts b/ServicesWeb/customer/src/utils/responseHandlers.ts new file mode 100644 index 0000000..45cfc42 --- /dev/null +++ b/ServicesWeb/customer/src/utils/responseHandlers.ts @@ -0,0 +1,91 @@ +import { NextResponse } from "next/server"; +import { ApiResponse, PaginationResponse, PaginatedApiResponse } from "./types"; + +/** + * Standard success response handler + * @param data The data to return in the response + * @param status HTTP status code (default: 200) + */ +export function successResponse(data: T, status: number = 200) { + return NextResponse.json( + { + success: true, + data, + } as ApiResponse, + { status } + ); +} + +/** + * Standard error response handler + * @param message Error message + * @param status HTTP status code (default: 500) + */ +export function errorResponse(message: string, status: number = 500) { + console.error(`API error: ${message}`); + return NextResponse.json( + { + success: false, + error: message + } as ApiResponse, + { status } + ); +} + +/** + * Standard pagination response format + * @param data Array of items to return + * @param pagination Pagination information + */ +export function paginationResponse(data: T[], pagination: PaginationResponse | null) { + return NextResponse.json({ + data: data || [], + pagination: pagination || { + page: 1, + size: 10, + totalCount: 0, + totalItems: 0, + totalPages: 0, + pageCount: 0, + orderField: ["name"], + orderType: ["asc"], + query: {}, + next: false, + back: false, + }, + } as PaginatedApiResponse); +} + +/** + * Create response handler + * @param data The created item data + */ +export function createResponse(data: T) { + return successResponse( + { + ...data as any, + createdAt: new Date().toISOString(), + } as T, + 201 + ); +} + +/** + * Update response handler + * @param data The updated item data + */ +export function updateResponse(data: T) { + return successResponse( + { + ...data as any, + updatedAt: new Date().toISOString(), + } as T + ); +} + +/** + * Delete response handler + */ +export function deleteResponse() { + return successResponse({ message: "Item deleted successfully" }, 204); +} diff --git a/ServicesWeb/customer/src/utils/types.ts b/ServicesWeb/customer/src/utils/types.ts new file mode 100644 index 0000000..990bf90 --- /dev/null +++ b/ServicesWeb/customer/src/utils/types.ts @@ -0,0 +1,119 @@ +/** + * Type definitions for API utilities + */ + +import { NextRequest } from "next/server"; + +/** + * Validation result interface + */ +export interface ValidationResult { + valid: boolean; + error?: string; +} + +/** + * Pagination parameters interface + */ +export interface PaginationParams { + page: number; + size: number; + orderField: string[]; + orderType: string[]; + query: Record; +} + +/** + * Pagination response interface + */ +export interface PaginationResponse { + page: number; + size: number; + totalCount: number; + totalItems: number; + totalPages: number; + pageCount: number; + orderField: string[]; + orderType: string[]; + query: Record; + next: boolean; + back: boolean; +} + +export const defaultPaginationResponse: PaginationResponse = { + page: 1, + size: 10, + totalCount: 0, + totalItems: 0, + totalPages: 0, + pageCount: 0, + orderField: ["uu_id"], + orderType: ["asc"], + query: {}, + next: false, + back: false, +}; + +/** + * API response interface + */ +export interface ApiResponse { + success: boolean; + data?: T; + error?: string; +} + +/** + * Paginated API response interface + */ +export interface PaginatedApiResponse { + data: T[]; + pagination: PaginationResponse; +} + +export const collectPaginationFromApiResponse = ( + response: PaginatedApiResponse +): PaginationResponse => { + return { + page: response.pagination?.page || 1, + size: response.pagination?.size || 10, + totalCount: response.pagination?.totalCount || 0, + totalItems: response.pagination?.totalItems || 0, + totalPages: response.pagination?.totalPages || 0, + pageCount: response.pagination?.pageCount || 0, + orderField: response.pagination?.orderField || ["uu_id"], + orderType: response.pagination?.orderType || ["asc"], + query: response.pagination?.query || {}, + next: response.pagination?.next || false, + back: response.pagination?.back || false, + }; +}; + +/** + * API handler function types + */ +export type ApiHandlerWithRequest = (request: NextRequest, body: any) => Promise; +export type ApiHandlerBodyOnly = (body: any) => Promise; +export type ApiHandler = ApiHandlerWithRequest | ApiHandlerBodyOnly; + +/** + * List function type + */ +export type ListFunction = ( + params: PaginationParams +) => Promise>; + +/** + * Create function type + */ +export type CreateFunction = (data: any) => Promise; + +/** + * Update function type + */ +export type UpdateFunction = (id: any, data: any) => Promise; + +/** + * Delete function type + */ +export type DeleteFunction = (id: any) => Promise; diff --git a/ServicesWeb/customer/src/validations/mutual/fecther/validations.ts b/ServicesWeb/customer/src/validations/mutual/fecther/validations.ts new file mode 100644 index 0000000..394dd2d --- /dev/null +++ b/ServicesWeb/customer/src/validations/mutual/fecther/validations.ts @@ -0,0 +1,44 @@ +interface FetcherRequest { + url: string; + isNoCache: boolean; +} + +interface PostFetcherRequest extends FetcherRequest { + body: Record; +} + +interface GetFetcherRequest extends FetcherRequest { + url: string; +} + +interface DeleteFetcherRequest extends GetFetcherRequest {} +interface PutFetcherRequest extends PostFetcherRequest {} +interface PatchFetcherRequest extends PostFetcherRequest {} + +interface FetcherRespose { + success: boolean; +} +interface PaginationResponse { + onPage: number; + onPageCount: number; + totalPage: number; + totalCount: number; + next: boolean; + back: boolean; +} + +interface FetcherDataResponse extends FetcherRespose { + data: Record | null; + pagination?: PaginationResponse; +} + +export type { + FetcherRequest, + PostFetcherRequest, + GetFetcherRequest, + DeleteFetcherRequest, + PutFetcherRequest, + PatchFetcherRequest, + FetcherRespose, + FetcherDataResponse, +}; diff --git a/ServicesWeb/management/src/components/ui/accordion.tsx b/ServicesWeb/management/src/components/mutual/ui/accordion.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/accordion.tsx rename to ServicesWeb/management/src/components/mutual/ui/accordion.tsx diff --git a/ServicesWeb/management/src/components/ui/alert-dialog.tsx b/ServicesWeb/management/src/components/mutual/ui/alert-dialog.tsx similarity index 98% rename from ServicesWeb/management/src/components/ui/alert-dialog.tsx rename to ServicesWeb/management/src/components/mutual/ui/alert-dialog.tsx index 0863e40..2dd7ac1 100644 --- a/ServicesWeb/management/src/components/ui/alert-dialog.tsx +++ b/ServicesWeb/management/src/components/mutual/ui/alert-dialog.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +import { buttonVariants } from "@/components/mutual/ui/button" function AlertDialog({ ...props diff --git a/ServicesWeb/management/src/components/ui/alert.tsx b/ServicesWeb/management/src/components/mutual/ui/alert.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/alert.tsx rename to ServicesWeb/management/src/components/mutual/ui/alert.tsx diff --git a/ServicesWeb/management/src/components/ui/aspect-ratio.tsx b/ServicesWeb/management/src/components/mutual/ui/aspect-ratio.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/aspect-ratio.tsx rename to ServicesWeb/management/src/components/mutual/ui/aspect-ratio.tsx diff --git a/ServicesWeb/management/src/components/ui/avatar.tsx b/ServicesWeb/management/src/components/mutual/ui/avatar.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/avatar.tsx rename to ServicesWeb/management/src/components/mutual/ui/avatar.tsx diff --git a/ServicesWeb/management/src/components/ui/badge.tsx b/ServicesWeb/management/src/components/mutual/ui/badge.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/badge.tsx rename to ServicesWeb/management/src/components/mutual/ui/badge.tsx diff --git a/ServicesWeb/management/src/components/ui/button.tsx b/ServicesWeb/management/src/components/mutual/ui/button.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/button.tsx rename to ServicesWeb/management/src/components/mutual/ui/button.tsx diff --git a/ServicesWeb/customer/src/components/ui/calendar.tsx b/ServicesWeb/management/src/components/mutual/ui/calendar.tsx similarity index 97% rename from ServicesWeb/customer/src/components/ui/calendar.tsx rename to ServicesWeb/management/src/components/mutual/ui/calendar.tsx index b8df044..541868a 100644 --- a/ServicesWeb/customer/src/components/ui/calendar.tsx +++ b/ServicesWeb/management/src/components/mutual/ui/calendar.tsx @@ -5,7 +5,7 @@ import { ChevronLeft, ChevronRight } from "lucide-react" import { DayPicker } from "react-day-picker" import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +import { buttonVariants } from "@/components/mutual/ui/button" function Calendar({ className, diff --git a/ServicesWeb/management/src/components/ui/card.tsx b/ServicesWeb/management/src/components/mutual/ui/card.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/card.tsx rename to ServicesWeb/management/src/components/mutual/ui/card.tsx diff --git a/ServicesWeb/management/src/components/ui/checkbox.tsx b/ServicesWeb/management/src/components/mutual/ui/checkbox.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/checkbox.tsx rename to ServicesWeb/management/src/components/mutual/ui/checkbox.tsx diff --git a/ServicesWeb/management/src/components/ui/collapsible.tsx b/ServicesWeb/management/src/components/mutual/ui/collapsible.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/collapsible.tsx rename to ServicesWeb/management/src/components/mutual/ui/collapsible.tsx diff --git a/ServicesWeb/management/src/components/ui/command.tsx b/ServicesWeb/management/src/components/mutual/ui/command.tsx similarity index 99% rename from ServicesWeb/management/src/components/ui/command.tsx rename to ServicesWeb/management/src/components/mutual/ui/command.tsx index 4ca5349..3467b11 100644 --- a/ServicesWeb/management/src/components/ui/command.tsx +++ b/ServicesWeb/management/src/components/mutual/ui/command.tsx @@ -11,7 +11,7 @@ import { DialogDescription, DialogHeader, DialogTitle, -} from "@/components/ui/dialog" +} from "@/components/mutual/ui/dialog" function Command({ className, diff --git a/ServicesWeb/management/src/components/ui/context-menu.tsx b/ServicesWeb/management/src/components/mutual/ui/context-menu.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/context-menu.tsx rename to ServicesWeb/management/src/components/mutual/ui/context-menu.tsx diff --git a/ServicesWeb/management/src/components/ui/dialog.tsx b/ServicesWeb/management/src/components/mutual/ui/dialog.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/dialog.tsx rename to ServicesWeb/management/src/components/mutual/ui/dialog.tsx diff --git a/ServicesWeb/management/src/components/ui/dropdown-menu.tsx b/ServicesWeb/management/src/components/mutual/ui/dropdown-menu.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/dropdown-menu.tsx rename to ServicesWeb/management/src/components/mutual/ui/dropdown-menu.tsx diff --git a/ServicesWeb/customer/src/components/ui/form.tsx b/ServicesWeb/management/src/components/mutual/ui/form.tsx similarity index 98% rename from ServicesWeb/customer/src/components/ui/form.tsx rename to ServicesWeb/management/src/components/mutual/ui/form.tsx index 524b986..8c65a07 100644 --- a/ServicesWeb/customer/src/components/ui/form.tsx +++ b/ServicesWeb/management/src/components/mutual/ui/form.tsx @@ -14,7 +14,7 @@ import { } from "react-hook-form" import { cn } from "@/lib/utils" -import { Label } from "@/components/ui/label" +import { Label } from "@/components/mutual/ui/label" const Form = FormProvider diff --git a/ServicesWeb/management/src/components/ui/hover-card.tsx b/ServicesWeb/management/src/components/mutual/ui/hover-card.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/hover-card.tsx rename to ServicesWeb/management/src/components/mutual/ui/hover-card.tsx diff --git a/ServicesWeb/management/src/components/ui/input.tsx b/ServicesWeb/management/src/components/mutual/ui/input.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/input.tsx rename to ServicesWeb/management/src/components/mutual/ui/input.tsx diff --git a/ServicesWeb/management/src/components/ui/label.tsx b/ServicesWeb/management/src/components/mutual/ui/label.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/label.tsx rename to ServicesWeb/management/src/components/mutual/ui/label.tsx diff --git a/ServicesWeb/management/src/components/ui/menubar.tsx b/ServicesWeb/management/src/components/mutual/ui/menubar.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/menubar.tsx rename to ServicesWeb/management/src/components/mutual/ui/menubar.tsx diff --git a/ServicesWeb/management/src/components/ui/navigation-menu.tsx b/ServicesWeb/management/src/components/mutual/ui/navigation-menu.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/navigation-menu.tsx rename to ServicesWeb/management/src/components/mutual/ui/navigation-menu.tsx diff --git a/ServicesWeb/management/src/components/ui/popover.tsx b/ServicesWeb/management/src/components/mutual/ui/popover.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/popover.tsx rename to ServicesWeb/management/src/components/mutual/ui/popover.tsx diff --git a/ServicesWeb/management/src/components/ui/progress.tsx b/ServicesWeb/management/src/components/mutual/ui/progress.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/progress.tsx rename to ServicesWeb/management/src/components/mutual/ui/progress.tsx diff --git a/ServicesWeb/management/src/components/ui/radio-group.tsx b/ServicesWeb/management/src/components/mutual/ui/radio-group.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/radio-group.tsx rename to ServicesWeb/management/src/components/mutual/ui/radio-group.tsx diff --git a/ServicesWeb/management/src/components/ui/scroll-area.tsx b/ServicesWeb/management/src/components/mutual/ui/scroll-area.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/scroll-area.tsx rename to ServicesWeb/management/src/components/mutual/ui/scroll-area.tsx diff --git a/ServicesWeb/management/src/components/ui/select.tsx b/ServicesWeb/management/src/components/mutual/ui/select.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/select.tsx rename to ServicesWeb/management/src/components/mutual/ui/select.tsx diff --git a/ServicesWeb/management/src/components/ui/separator.tsx b/ServicesWeb/management/src/components/mutual/ui/separator.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/separator.tsx rename to ServicesWeb/management/src/components/mutual/ui/separator.tsx diff --git a/ServicesWeb/management/src/components/ui/sheet.tsx b/ServicesWeb/management/src/components/mutual/ui/sheet.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/sheet.tsx rename to ServicesWeb/management/src/components/mutual/ui/sheet.tsx diff --git a/ServicesWeb/management/src/components/ui/skeleton.tsx b/ServicesWeb/management/src/components/mutual/ui/skeleton.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/skeleton.tsx rename to ServicesWeb/management/src/components/mutual/ui/skeleton.tsx diff --git a/ServicesWeb/management/src/components/ui/slider.tsx b/ServicesWeb/management/src/components/mutual/ui/slider.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/slider.tsx rename to ServicesWeb/management/src/components/mutual/ui/slider.tsx diff --git a/ServicesWeb/management/src/components/ui/sonner.tsx b/ServicesWeb/management/src/components/mutual/ui/sonner.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/sonner.tsx rename to ServicesWeb/management/src/components/mutual/ui/sonner.tsx diff --git a/ServicesWeb/management/src/components/ui/switch.tsx b/ServicesWeb/management/src/components/mutual/ui/switch.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/switch.tsx rename to ServicesWeb/management/src/components/mutual/ui/switch.tsx diff --git a/ServicesWeb/management/src/components/ui/table.tsx b/ServicesWeb/management/src/components/mutual/ui/table.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/table.tsx rename to ServicesWeb/management/src/components/mutual/ui/table.tsx diff --git a/ServicesWeb/management/src/components/ui/tabs.tsx b/ServicesWeb/management/src/components/mutual/ui/tabs.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/tabs.tsx rename to ServicesWeb/management/src/components/mutual/ui/tabs.tsx diff --git a/ServicesWeb/management/src/components/ui/textarea.tsx b/ServicesWeb/management/src/components/mutual/ui/textarea.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/textarea.tsx rename to ServicesWeb/management/src/components/mutual/ui/textarea.tsx diff --git a/ServicesWeb/management/src/components/ui/toggle.tsx b/ServicesWeb/management/src/components/mutual/ui/toggle.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/toggle.tsx rename to ServicesWeb/management/src/components/mutual/ui/toggle.tsx diff --git a/ServicesWeb/management/src/components/ui/tooltip.tsx b/ServicesWeb/management/src/components/mutual/ui/tooltip.tsx similarity index 100% rename from ServicesWeb/management/src/components/ui/tooltip.tsx rename to ServicesWeb/management/src/components/mutual/ui/tooltip.tsx diff --git a/api_env.env b/api_env.env new file mode 100644 index 0000000..c1cddc1 --- /dev/null +++ b/api_env.env @@ -0,0 +1,41 @@ +MONGO_ENGINE=mongodb +MONGO_DB=appdb +MONGO_HOST=10.10.2.13 +MONGO_PORT=27017 +MONGO_USER=appuser +MONGO_AUTH_DB=appdb +MONGO_PASSWORD=apppassword + +POSTGRES_USER=postgres +POSTGRES_PASSWORD=password +POSTGRES_DB=postgres +POSTGRES_HOST=10.10.2.14 +POSTGRES_PORT=5432 +POSTGRES_ENGINE=postgresql+psycopg2 +POSTGRES_POOL_PRE_PING=True +POSTGRES_POOL_SIZE=20 +POSTGRES_MAX_OVERFLOW=10 +POSTGRES_POOL_RECYCLE=600 +POSTGRES_POOL_TIMEOUT=30 +POSTGRES_ECHO=True + +REDIS_HOST=10.10.2.15 +REDIS_PASSWORD=your_strong_password_here +REDIS_PORT=6379 +REDIS_DB=0 + +API_ACCESS_EMAIL_EXT=evyos.com.tr +API_ALGORITHM=HS256 +API_ACCESS_TOKEN_LENGTH=90 +API_REFRESHER_TOKEN_LENGTH=144 +API_EMAIL_HOST=10.10.2.36 +API_DATETIME_FORMAT=YYYY-MM-DD HH:mm:ss Z +API_FORGOT_LINK=https://www.evyos.com.tr/password/create?tokenUrl= +API_VERSION=0.1.001 +API_ACCESS_TOKEN_TAG=eys-acs-tkn + +EMAIL_HOST=10.10.2.34 +EMAIL_USERNAME=karatay@mehmetkaratay.com.tr +EMAIL_PASSWORD=system +EMAIL_PORT=587 +EMAIL_SEND=0 diff --git a/docker-compose.yml b/docker-compose.yml index 157eb0f..117de7a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,28 @@ services: # cpus: 1 # mem_limit: 2048m + tester_service: + container_name: tester_service + build: + context: . + dockerfile: ServicesApi/Builds/TestApi/Dockerfile + env_file: + - api_env.env + networks: + - wag-services + environment: + - API_PATH=app:app + - API_HOST=0.0.0.0 + - API_PORT=8005 + - API_LOG_LEVEL=info + - API_RELOAD=1 + - API_APP_NAME=evyos-tester-api-gateway + - API_TITLE=WAG API Tester Api Gateway + - API_DESCRIPTION=This api is serves as web tester api gateway only to evyos web services. + - API_APP_URL=https://tester_service + ports: + - "8005:8005" + # account_service: # container_name: account_service # build: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5d4e085 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "prod-wag-backend-automate-services" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "alembic>=1.15.2", + "arrow>=1.3.0", + "cryptography>=44.0.2", + "faker>=37.1.0", + "fastapi>=0.115.12", + "pandas>=2.2.3", + "prometheus-fastapi-instrumentator>=7.1.0", + "psycopg2-binary>=2.9.10", + "pydantic-settings>=2.8.1", + "pymongo>=4.11.3", + "pytest>=8.3.5", + "redbox>=0.2.1", + "redis>=5.2.1", + "redmail>=0.6.0", + "requests>=2.32.3", + "sqlalchemy-mixins>=2.0.5", + "textdistance>=4.6.3", + "unidecode>=1.3.8", + "uvicorn>=0.34.0", +]