updated Service providers
This commit is contained in:
parent
fa4df11323
commit
60507c87e0
|
|
@ -0,0 +1,30 @@
|
|||
ToDo List:
|
||||
|
||||
Redis Items:
|
||||
|
||||
Internal serves from Class inside API:
|
||||
- [ ] Save Events activities as [
|
||||
{"endpoint_code-UUID1": ["event-UUID1", "event-UUID2", "event-UUID3"]},
|
||||
{"endpoint_code-UUID2": ["event-UUID4", "event-UUID5", "event-UUID6"]}
|
||||
]
|
||||
|
||||
|
||||
- [ ] Save Application activities as [
|
||||
{"app1": ["appCode-UUID1", "appCode-UUID2", "appCode-UUID3"]},
|
||||
{"app2": ["appCode-UUID4", "appCode-UUID5", "appCode-UUID6"]}
|
||||
]
|
||||
|
||||
|
||||
Serve Only from Redis:
|
||||
- [] Endpoint: "authentication/page/valid"
|
||||
send data: {"page": "app1"}
|
||||
result: {"page": "appCode-UUID4"}
|
||||
|
||||
|
||||
Client side Frontend:
|
||||
On Redirect [send=> authentication/page/valid]
|
||||
Retrieve available application UUIDs from API
|
||||
Redirect to related page with UUID
|
||||
{
|
||||
"appCode-UUID1": App1.tsx
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ from ApiServices.AuthService.validations.request.authentication.login_post impor
|
|||
RequestForgotPasswordPhone,
|
||||
RequestForgotPasswordEmail,
|
||||
RequestVerifyOTP,
|
||||
RequestApplication,
|
||||
)
|
||||
from ApiServices.AuthService.events.auth.auth import AuthHandlers
|
||||
|
||||
|
|
@ -367,7 +368,45 @@ def authentication_password_verify_otp(
|
|||
"tz": tz or "GMT+3",
|
||||
"token": token,
|
||||
}
|
||||
print('Token&OTP : ', data.otp, data.token)
|
||||
print("Token&OTP : ", data.otp, data.token)
|
||||
if not domain or not language:
|
||||
return JSONResponse(
|
||||
content={"error": "EYS_0003"},
|
||||
status_code=status.HTTP_406_NOT_ACCEPTABLE,
|
||||
headers=headers,
|
||||
)
|
||||
return JSONResponse(
|
||||
content={},
|
||||
status_code=status.HTTP_202_ACCEPTED,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
|
||||
@auth_route.post(
|
||||
path="/page/valid",
|
||||
summary="Verify if page is valid returns application avaliable",
|
||||
description="Verify if page is valid returns application avaliable",
|
||||
)
|
||||
def authentication_page_valid(
|
||||
request: Request,
|
||||
data: RequestApplication,
|
||||
language: str = Header(None, alias="language"),
|
||||
domain: str = Header(None, alias="domain"),
|
||||
tz: str = Header(None, alias="timezone"),
|
||||
):
|
||||
"""
|
||||
Verify if page is valid returns application that can user reach
|
||||
page: { url = /building/create}
|
||||
result: { "application": "4c11f5ef-0bbd-41ac-925e-f79d9aac2b0e" }
|
||||
"""
|
||||
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
|
||||
headers = {
|
||||
"language": language or "",
|
||||
"domain": domain or "",
|
||||
"eys-ext": f"{str(uuid.uuid4())}",
|
||||
"tz": tz or "GMT+3",
|
||||
"token": token,
|
||||
}
|
||||
if not domain or not language:
|
||||
return JSONResponse(
|
||||
content={"error": "EYS_0003"},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ class RequestVerifyOTP(BaseModel):
|
|||
otp: str
|
||||
|
||||
|
||||
class RequestApplication(BaseModel):
|
||||
page: str # /building/create
|
||||
|
||||
|
||||
class RequestSelectOccupant(BaseModel):
|
||||
|
||||
company_uu_id: str
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from fastapi import APIRouter, Request, Response
|
||||
import uuid
|
||||
from fastapi import APIRouter, Request, Response, Header
|
||||
|
||||
from ApiServices.TemplateService.config import api_config
|
||||
from ApiServices.TemplateService.events.template.event import template_event_cluster
|
||||
|
||||
test_template_route = APIRouter(prefix="/test", tags=["Test"])
|
||||
|
|
@ -10,10 +12,25 @@ test_template_route = APIRouter(prefix="/test", tags=["Test"])
|
|||
description="Test Template Route",
|
||||
operation_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5",
|
||||
)
|
||||
def test_template(request: Request, response: Response):
|
||||
def test_template(
|
||||
request: Request,
|
||||
response: Response,
|
||||
language: str = Header(None, alias="language"),
|
||||
domain: str = Header(None, alias="domain"),
|
||||
tz: str = Header(None, alias="timezone"),
|
||||
):
|
||||
"""
|
||||
Test Template Route
|
||||
"""
|
||||
event_code = "bb20c8c6-a289-4cab-9da7-34ca8a36c8e5"
|
||||
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
|
||||
headers = {
|
||||
"language": language or "",
|
||||
"domain": domain or "",
|
||||
"eys-ext": f"{str(uuid.uuid4())}",
|
||||
"tz": tz or "GMT+3",
|
||||
"token": token,
|
||||
}
|
||||
event_cluster_matched = template_event_cluster.match_event(
|
||||
event_keys=[
|
||||
"3f510dcf-9f84-4eb9-b919-f582f30adab1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,210 @@
|
|||
import enum
|
||||
|
||||
from typing import Optional, Union, Dict, Any, List
|
||||
from pydantic import BaseModel
|
||||
from Controllers.Redis.database import RedisActions
|
||||
from ..config import api_config
|
||||
|
||||
|
||||
class UserType(enum.Enum):
|
||||
|
||||
employee = 1
|
||||
occupant = 2
|
||||
|
||||
|
||||
class Credentials(BaseModel):
|
||||
|
||||
person_id: int
|
||||
person_name: str
|
||||
|
||||
|
||||
class ApplicationToken(BaseModel):
|
||||
# Application Token Object -> is the main object for the user
|
||||
|
||||
user_type: int = UserType.occupant.value
|
||||
credential_token: str = ""
|
||||
|
||||
user_uu_id: str
|
||||
user_id: int
|
||||
|
||||
person_id: int
|
||||
person_uu_id: str
|
||||
|
||||
request: Optional[dict] = None # Request Info of Client
|
||||
expires_at: Optional[float] = None # Expiry timestamp
|
||||
|
||||
|
||||
class OccupantToken(BaseModel):
|
||||
|
||||
# Selection of the occupant type for a build part is made by the user
|
||||
|
||||
living_space_id: int # Internal use
|
||||
living_space_uu_id: str # Outer use
|
||||
|
||||
occupant_type_id: int
|
||||
occupant_type_uu_id: str
|
||||
occupant_type: str
|
||||
|
||||
build_id: int
|
||||
build_uuid: str
|
||||
build_part_id: int
|
||||
build_part_uuid: str
|
||||
|
||||
responsible_company_id: Optional[int] = None
|
||||
responsible_company_uuid: Optional[str] = None
|
||||
responsible_employee_id: Optional[int] = None
|
||||
responsible_employee_uuid: Optional[str] = None
|
||||
|
||||
reachable_event_codes: Optional[dict[str, list[str]]] = (
|
||||
None # ID list of reachable modules
|
||||
)
|
||||
reachable_app_codes: Optional[dict[str, list[str]]] = (
|
||||
None # ID list of reachable modules
|
||||
)
|
||||
|
||||
|
||||
class CompanyToken(BaseModel):
|
||||
|
||||
# Selection of the company for an employee is made by the user
|
||||
company_id: int
|
||||
company_uu_id: str
|
||||
|
||||
department_id: int # ID list of departments
|
||||
department_uu_id: str # ID list of departments
|
||||
|
||||
duty_id: int
|
||||
duty_uu_id: str
|
||||
|
||||
staff_id: int
|
||||
staff_uu_id: str
|
||||
|
||||
employee_id: int
|
||||
employee_uu_id: str
|
||||
|
||||
bulk_duties_id: int
|
||||
|
||||
reachable_event_codes: Optional[dict[str, list[str]]] = (
|
||||
None # ID list of reachable modules
|
||||
)
|
||||
reachable_app_codes: Optional[dict[str, list[str]]] = (
|
||||
None # ID list of reachable modules
|
||||
)
|
||||
|
||||
|
||||
class OccupantTokenObject(ApplicationToken):
|
||||
# Occupant Token Object -> Requires selection of the occupant type for a specific build part
|
||||
|
||||
available_occupants: dict = None
|
||||
selected_occupant: Optional[OccupantToken] = None # Selected Occupant Type
|
||||
|
||||
@property
|
||||
def is_employee(self) -> bool:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_occupant(self) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
class EmployeeTokenObject(ApplicationToken):
|
||||
# Full hierarchy Employee[staff_id] -> Staff -> Duty -> Department -> Company
|
||||
|
||||
companies_id_list: List[int] # List of company objects
|
||||
companies_uu_id_list: List[str] # List of company objects
|
||||
|
||||
duty_id_list: List[int] # List of duty objects
|
||||
duty_uu_id_list: List[str] # List of duty objects
|
||||
|
||||
selected_company: Optional[CompanyToken] = None # Selected Company Object
|
||||
|
||||
@property
|
||||
def is_employee(self) -> bool:
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_occupant(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
|
||||
|
||||
|
||||
class TokenProvider:
|
||||
|
||||
AUTH_KEY: str = api_config.ACCESS_TOKEN_TAG
|
||||
AUTH_TOKEN: str = "AUTH_TOKEN"
|
||||
|
||||
@classmethod
|
||||
def process_redis_object(cls, redis_object: Dict[str, Any]) -> TokenDictType:
|
||||
"""
|
||||
Process Redis object and return appropriate token object.
|
||||
"""
|
||||
if not redis_object.get("selected_company"):
|
||||
redis_object["selected_company"] = None
|
||||
if not redis_object.get("selected_occupant"):
|
||||
redis_object["selected_occupant"] = None
|
||||
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_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.process_redis_object(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.process_redis_object(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, tokens: Union[TokenDictType, List[TokenDictType]]
|
||||
):
|
||||
if isinstance(tokens, TokenDictType):
|
||||
if application_codes := tokens.reachable_app_codes.get(page_url, None):
|
||||
return application_codes
|
||||
elif isinstance(tokens, list):
|
||||
retrieved_event_apps = []
|
||||
for token in tokens:
|
||||
if application_codes := token.reachable_app_codes.get(page_url, None):
|
||||
retrieved_event_apps.append(application_codes)
|
||||
return retrieved_event_apps
|
||||
raise ValueError("Invalid token type or no application codes found.")
|
||||
|
||||
@classmethod
|
||||
def retrieve_event_codes(
|
||||
cls, endpoint_code: str, tokens: Union[TokenDictType, List[TokenDictType]]
|
||||
):
|
||||
if isinstance(tokens, TokenDictType):
|
||||
if event_codes := tokens.reachable_event_codes.get(endpoint_code, None):
|
||||
return event_codes
|
||||
elif isinstance(tokens, List):
|
||||
retrieved_event_codes = []
|
||||
for token in tokens:
|
||||
if isinstance(token, TokenDictType):
|
||||
if event_codes := token.reachable_event_codes.get(
|
||||
endpoint_code, None
|
||||
):
|
||||
retrieved_event_codes.append(event_codes)
|
||||
return retrieved_event_codes
|
||||
raise ValueError("Invalid token type or no event codes found.")
|
||||
|
|
@ -8,9 +8,15 @@
|
|||
"name": "client-frontend",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"flatpickr": "^4.6.13",
|
||||
"next": "15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"sonner": "^2.0.3",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
|
|
@ -42,6 +48,17 @@
|
|||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz",
|
||||
"integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==",
|
||||
"dependencies": {
|
||||
"@standard-schema/utils": "^0.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-hook-form": "^7.55.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
||||
|
|
@ -509,6 +526,11 @@
|
|||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@standard-schema/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
|
|
@ -876,6 +898,11 @@
|
|||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flatpickr": {
|
||||
"version": "4.6.13",
|
||||
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
|
||||
"integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
|
|
@ -1195,6 +1222,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-crypto": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/next-crypto/-/next-crypto-1.0.8.tgz",
|
||||
"integrity": "sha512-6VcrH+xFuuCRGCdDMjFFibhJ97c4s+J/6SEV73RUYJhh38MDW4WXNZNTWIMZBq0B29LOIfAQ0XA37xGUZZCCjA=="
|
||||
},
|
||||
"node_modules/next/node_modules/postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
|
|
@ -1274,6 +1306,21 @@
|
|||
"react": "^19.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.55.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.55.0.tgz",
|
||||
"integrity": "sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog==",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/react-hook-form"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||
|
|
@ -1339,6 +1386,15 @@
|
|||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/sonner": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz",
|
||||
"integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
|
@ -1415,6 +1471,14 @@
|
|||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.24.2",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
|
||||
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,16 +9,22 @@
|
|||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"flatpickr": "^4.6.13",
|
||||
"next": "15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"next": "15.2.4"
|
||||
"react-hook-form": "^7.55.0",
|
||||
"sonner": "^2.0.3",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"tailwindcss": "^4"
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
"use server";
|
||||
// import { retrieveAccessToken } from "@/apicalls/cookies/token";
|
||||
|
||||
const defaultHeaders = {
|
||||
accept: "application/json",
|
||||
language: "tr",
|
||||
domain: "evyos.com.tr",
|
||||
tz: "GMT+3",
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
|
||||
const DefaultResponse = {
|
||||
error: "Hata tipi belirtilmedi",
|
||||
status: "500",
|
||||
data: {},
|
||||
};
|
||||
|
||||
const cacheList = ["no-cache", "no-store", "force-cache", "only-if-cached"];
|
||||
|
||||
const prepareResponse = (response: any, statusCode: number) => {
|
||||
try {
|
||||
return {
|
||||
status: statusCode,
|
||||
data: response || {},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error preparing response:", error);
|
||||
return {
|
||||
...DefaultResponse,
|
||||
error: "Response parsing error",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = async (
|
||||
endpoint: string,
|
||||
payload: any,
|
||||
method: string = "POST",
|
||||
cache: boolean = false
|
||||
) => {
|
||||
try {
|
||||
const headers = {
|
||||
...defaultHeaders,
|
||||
};
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
cache: cache ? "force-cache" : "no-cache",
|
||||
};
|
||||
|
||||
if (method !== "GET" && payload) {
|
||||
fetchOptions.body = JSON.stringify(payload);
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, fetchOptions);
|
||||
const responseJson = await response.json();
|
||||
console.log("Fetching:", endpoint, fetchOptions);
|
||||
console.log("Response:", responseJson);
|
||||
return prepareResponse(responseJson, response.status);
|
||||
} catch (error) {
|
||||
console.error("Fetch error:", error);
|
||||
return {
|
||||
...DefaultResponse,
|
||||
error: error instanceof Error ? error.message : "Network error",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const updateDataWithToken = async (
|
||||
endpoint: string,
|
||||
uuid: string,
|
||||
payload: any,
|
||||
method: string = "POST",
|
||||
cache: boolean = false
|
||||
) => {
|
||||
// const accessToken = (await retrieveAccessToken()) || "";
|
||||
const headers = {
|
||||
...defaultHeaders,
|
||||
// "evyos-session-key": accessToken,
|
||||
};
|
||||
|
||||
try {
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
cache: cache ? "force-cache" : "no-cache",
|
||||
};
|
||||
|
||||
if (method !== "GET" && payload) {
|
||||
fetchOptions.body = JSON.stringify(payload.payload);
|
||||
}
|
||||
|
||||
const response = await fetch(`${endpoint}/${uuid}`, fetchOptions);
|
||||
const responseJson = await response.json();
|
||||
console.log("Fetching:", `${endpoint}/${uuid}`, fetchOptions);
|
||||
console.log("Response:", responseJson);
|
||||
return prepareResponse(responseJson, response.status);
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
return {
|
||||
...DefaultResponse,
|
||||
error: error instanceof Error ? error.message : "Network error",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDataWithToken = async (
|
||||
endpoint: string,
|
||||
payload: any,
|
||||
method: string = "POST",
|
||||
cache: boolean = false
|
||||
) => {
|
||||
// const accessToken = (await retrieveAccessToken()) || "";
|
||||
const headers = {
|
||||
...defaultHeaders,
|
||||
// "evyos-session-key": accessToken,
|
||||
};
|
||||
|
||||
try {
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
cache: cache ? "force-cache" : "no-cache",
|
||||
};
|
||||
|
||||
if (method !== "GET" && payload) {
|
||||
fetchOptions.body = JSON.stringify(payload);
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, fetchOptions);
|
||||
const responseJson = await response.json();
|
||||
console.log("Fetching:", endpoint, fetchOptions);
|
||||
console.log("Response:", responseJson);
|
||||
return prepareResponse(responseJson, response.status);
|
||||
} catch (error) {
|
||||
console.error("Fetch with token error:", error);
|
||||
return {
|
||||
...DefaultResponse,
|
||||
error: error instanceof Error ? error.message : "Network error",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export { fetchData, fetchDataWithToken, updateDataWithToken };
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
const formatServiceUrl = (url: string) => {
|
||||
if (!url) return "";
|
||||
return url.startsWith("http") ? url : `http://${url}`;
|
||||
};
|
||||
|
||||
export const baseUrlAuth = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "auth_service:8001"
|
||||
);
|
||||
// export const baseUrlValidation = formatServiceUrl(
|
||||
// process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "validationservice:8888"
|
||||
// );
|
||||
// export const baseUrlEvent = formatServiceUrl(
|
||||
// process.env.NEXT_PUBLIC_EVENT_SERVICE_URL || "eventservice:8888"
|
||||
// );
|
||||
export const tokenSecret = process.env.TOKENSECRET_90 || "";
|
||||
|
||||
export const cookieObject: any = {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "none",
|
||||
secure: true,
|
||||
maxAge: 3600,
|
||||
priority: "high",
|
||||
};
|
||||
|
||||
interface FilterListInterface {
|
||||
page?: number | null | undefined;
|
||||
size?: number | null | undefined;
|
||||
orderField?: string | null | undefined;
|
||||
orderType?: string | null | undefined;
|
||||
includeJoins?: any[] | null | undefined;
|
||||
query?: any | null | undefined;
|
||||
}
|
||||
|
||||
class FilterList {
|
||||
page: number;
|
||||
size: number;
|
||||
orderField: string;
|
||||
orderType: string;
|
||||
includeJoins: any[];
|
||||
query: any;
|
||||
|
||||
constructor({
|
||||
page = 1,
|
||||
size = 5,
|
||||
orderField = "id",
|
||||
orderType = "asc",
|
||||
includeJoins = [],
|
||||
query = {},
|
||||
}: FilterListInterface = {}) {
|
||||
this.page = page ?? 1;
|
||||
this.size = size ?? 5;
|
||||
this.orderField = orderField ?? "uu_id";
|
||||
this.orderType = orderType ?? "asc";
|
||||
this.orderType = this.orderType.startsWith("a") ? "asc" : "desc";
|
||||
this.includeJoins = includeJoins ?? [];
|
||||
this.query = query ?? {};
|
||||
}
|
||||
|
||||
filter() {
|
||||
return {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
orderField: this.orderField,
|
||||
orderType: this.orderType,
|
||||
includeJoins: this.includeJoins,
|
||||
query: this.query,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const defaultFilterList = new FilterList({});
|
||||
|
||||
export { FilterList, defaultFilterList };
|
||||
export type { FilterListInterface };
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
"use server";
|
||||
import NextCrypto from "next-crypto";
|
||||
|
||||
import { fetchData, fetchDataWithToken } from "../api-fetcher";
|
||||
import { baseUrlAuth, cookieObject, tokenSecret } from "../basics";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
// import { setAvailableEvents } from "../events/available";
|
||||
|
||||
const loginEndpoint = `${baseUrlAuth}/authentication/login`;
|
||||
const loginSelectEndpoint = `${baseUrlAuth}/authentication/select`;
|
||||
|
||||
console.log("loginEndpoint", loginEndpoint);
|
||||
console.log("loginSelectEndpoint", loginSelectEndpoint);
|
||||
|
||||
interface LoginViaAccessKeys {
|
||||
accessKey: string;
|
||||
password: string;
|
||||
rememberMe: boolean;
|
||||
}
|
||||
|
||||
interface LoginSelectEmployee {
|
||||
company_uu_id: string;
|
||||
}
|
||||
|
||||
interface LoginSelectOccupant {
|
||||
selectedBuilding: any;
|
||||
build_part_uu_id: string;
|
||||
occupant_uu_id: string;
|
||||
}
|
||||
|
||||
async function loginViaAccessKeys(payload: LoginViaAccessKeys) {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
const response = await fetchData(
|
||||
loginEndpoint,
|
||||
{
|
||||
access_key: payload.accessKey,
|
||||
password: payload.password,
|
||||
remember_me: payload.rememberMe,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
console.log("response", response);
|
||||
if (response.status === 200 || response.status === 202) {
|
||||
const loginRespone = response?.data;
|
||||
const accessToken = await nextCrypto.encrypt(loginRespone.access_token);
|
||||
const accessObject = await nextCrypto.encrypt(
|
||||
JSON.stringify(loginRespone.selection_list)
|
||||
);
|
||||
const userProfile = await nextCrypto.encrypt(
|
||||
JSON.stringify(loginRespone.user)
|
||||
);
|
||||
|
||||
cookieStore.set({
|
||||
name: "accessToken",
|
||||
value: accessToken,
|
||||
...cookieObject,
|
||||
});
|
||||
|
||||
cookieStore.set({
|
||||
name: "accessObject",
|
||||
value: accessObject,
|
||||
...cookieObject,
|
||||
});
|
||||
cookieStore.set({
|
||||
name: "userProfile",
|
||||
value: JSON.stringify(userProfile),
|
||||
...cookieObject,
|
||||
});
|
||||
try {
|
||||
return {
|
||||
completed: true,
|
||||
message: "Login successful",
|
||||
status: 200,
|
||||
data: loginRespone,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("JSON parse error:", error);
|
||||
return {
|
||||
completed: false,
|
||||
message: "Login NOT successful",
|
||||
status: 401,
|
||||
data: "{}",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
completed: false,
|
||||
// error: response.error || "Login failed",
|
||||
// message: response.message || "Authentication failed",
|
||||
status: response.status || 500,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
return {
|
||||
completed: false,
|
||||
// error: error instanceof Error ? error.message : "Login error",
|
||||
// message: "An error occurred during login",
|
||||
status: 500,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function loginSelectEmployee(payload: LoginSelectEmployee) {
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
const selectResponse: any = await fetchDataWithToken(
|
||||
loginSelectEndpoint,
|
||||
{
|
||||
company_uu_id: payload.company_uu_id,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
if (selectResponse.status === 200) {
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
company_uu_id: payload.company_uu_id,
|
||||
user_type: "employee",
|
||||
})
|
||||
);
|
||||
cookieStore.set({
|
||||
name: "userSelection",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
}
|
||||
return selectResponse;
|
||||
}
|
||||
|
||||
async function loginSelectOccupant(payload: LoginSelectOccupant) {
|
||||
const selectedBuilding = payload.selectedBuilding;
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
const selectResponse: any = await fetchDataWithToken(
|
||||
loginSelectEndpoint,
|
||||
{
|
||||
build_part_uu_id: payload.build_part_uu_id,
|
||||
occupant_uu_id: payload.occupant_uu_id,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
|
||||
if (selectResponse.status === 200) {
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
company_uu_id: {
|
||||
build_part_uu_id: payload.build_part_uu_id,
|
||||
occupant_uu_id: payload.occupant_uu_id,
|
||||
build_id: selectedBuilding,
|
||||
},
|
||||
user_type: "occupant",
|
||||
})
|
||||
);
|
||||
cookieStore.set({
|
||||
name: "userSelection",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
// await setAvailableEvents();
|
||||
}
|
||||
return selectResponse;
|
||||
}
|
||||
|
||||
export { loginViaAccessKeys, loginSelectEmployee, loginSelectOccupant };
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import React from "react";
|
||||
import Login from "@/components/auth/login";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "WAG Login",
|
||||
description: "Login to WAG system",
|
||||
};
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-100">
|
||||
<Login />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import React from "react";
|
||||
|
||||
function SelectPage() {
|
||||
return <div>SelectPage</div>;
|
||||
}
|
||||
|
||||
export default SelectPage;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "WAG Frontend",
|
||||
description: "WAG Frontend Application",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body>
|
||||
<div className="min-h-screen flex">
|
||||
<div className="w-1/3 bg-purple-600 p-8 text-white">
|
||||
<h1 className="text-4xl font-bold mb-4">Welcome to evyos</h1>
|
||||
<p className="text-lg">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
|
||||
enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||
nisi ut aliquip ex ea commodo consequat.
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-2/3">
|
||||
<Suspense fallback={<div>Loading...</div>}>{children}</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,17 +1,6 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
|
|
@ -24,11 +13,7 @@ export default function RootLayout({
|
|||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,103 +1,23 @@
|
|||
import Image from "next/image";
|
||||
export default async function Home() {
|
||||
const result = await fetch("http://auth_service:8001/authentication/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
language: "tr",
|
||||
domain: "evyos.com.tr",
|
||||
tz: "GMT+3",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
access_key: "karatay.berkay.sup@evyos.com.tr",
|
||||
password: "string",
|
||||
remember_me: true,
|
||||
}),
|
||||
});
|
||||
const data = await result.json();
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
{JSON.stringify({ data: data?.data })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
"use client";
|
||||
import { useState, useTransition } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { loginViaAccessKeys } from "@/apicalls/login/login";
|
||||
import { z } from "zod";
|
||||
|
||||
const loginSchema = z.object({
|
||||
email: z.string().email("Invalid email address"),
|
||||
password: z.string().min(5, "Password must be at least 5 characters"),
|
||||
});
|
||||
|
||||
type LoginFormData = z.infer<typeof loginSchema>;
|
||||
|
||||
function Login() {
|
||||
// Open transition for form login
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [jsonText, setJsonText] = useState<string | null>(null);
|
||||
|
||||
const Router = useRouter();
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
} = useForm<LoginFormData>({
|
||||
resolver: zodResolver(loginSchema),
|
||||
});
|
||||
|
||||
const onSubmit = async (data: LoginFormData) => {
|
||||
try {
|
||||
startTransition(() => {
|
||||
try {
|
||||
loginViaAccessKeys({
|
||||
accessKey: data.email,
|
||||
password: data.password,
|
||||
rememberMe: false,
|
||||
})
|
||||
.then((result: any) => {
|
||||
const dataResponse = result?.data;
|
||||
if (dataResponse?.access_token) {
|
||||
setJsonText(JSON.stringify(dataResponse));
|
||||
setTimeout(() => {
|
||||
Router.push("/auth/select");
|
||||
}, 2000);
|
||||
}
|
||||
return dataResponse;
|
||||
})
|
||||
.catch(() => {});
|
||||
} catch (error) {}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
setError("An error occurred during login");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full min-h-[inherit] flex-col items-center justify-center gap-4">
|
||||
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
|
||||
<h2 className="mb-6 text-center text-2xl font-bold text-gray-900">
|
||||
Login
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
{...register("email")}
|
||||
type="email"
|
||||
id="email"
|
||||
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500"
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="mt-1 text-sm text-red-600">
|
||||
{errors.email.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
{...register("password")}
|
||||
type="password"
|
||||
id="password"
|
||||
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500"
|
||||
/>
|
||||
{errors.password && (
|
||||
<p className="mt-1 text-sm text-red-600">
|
||||
{errors.password.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="rounded-md bg-red-50 p-4">
|
||||
<p className="text-sm text-red-700">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="w-full rounded-md bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50"
|
||||
>
|
||||
{isPending ? "Logging in..." : "Login"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{jsonText && (
|
||||
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
|
||||
<h2 className="mb-4 text-center text-xl font-bold text-gray-900">
|
||||
Response Data
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
{Object.entries(JSON.parse(jsonText)).map(([key, value]) => (
|
||||
<div key={key} className="flex items-start gap-2">
|
||||
<strong className="text-gray-700">{key}:</strong>
|
||||
<span className="text-gray-600">
|
||||
{typeof value === "object"
|
||||
? JSON.stringify(value)
|
||||
: value?.toString() || "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
||||
|
|
@ -18,4 +18,4 @@ COPY WebServices/management-frontend ./WebServices/management-frontend
|
|||
EXPOSE 3000
|
||||
|
||||
# Command to run the app
|
||||
CMD ["sh", "-c", "cd /WebServices/management-frontend && npm run dev"]
|
||||
CMD ["sh", "-c", "cd /WebServices/management-frontend && npm run dev"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
services:
|
||||
|
||||
mongo_service:
|
||||
container_name: mongo_service
|
||||
image: "bitnami/mongodb:latest"
|
||||
|
|
@ -59,70 +58,66 @@ services:
|
|||
- wag-services
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- .WebServices/client-frontend:WebServices/client-frontend
|
||||
- WebServices/client-frontend/node_modules
|
||||
- WebServices/client-frontend/.next
|
||||
# volumes:
|
||||
# - client-frontend:/WebServices/client-frontend
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
|
||||
management_frontend:
|
||||
container_name: management_frontend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: WebServices/management-frontend/Dockerfile
|
||||
networks:
|
||||
- wag-services
|
||||
ports:
|
||||
- "3001:3000" # Using different host port to avoid conflicts
|
||||
volumes:
|
||||
- .WebServices/management-frontend:WebServices/management-frontend
|
||||
- WebServices/management-frontend/node_modules
|
||||
- WebServices/management-frontend/.next
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
# management_frontend:
|
||||
# container_name: management_frontend
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: WebServices/management-frontend/Dockerfile
|
||||
# networks:
|
||||
# - wag-services
|
||||
# ports:
|
||||
# - "3001:3000" # Using different host port to avoid conflicts
|
||||
# # volumes:
|
||||
# # - management-frontend:/WebServices/management-frontend
|
||||
# environment:
|
||||
# - NODE_ENV=development
|
||||
|
||||
# initializer_service:
|
||||
# container_name: initializer_service
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ApiServices/InitialService/Dockerfile
|
||||
# networks:
|
||||
# - wag-services
|
||||
# env_file:
|
||||
# - api_env.env
|
||||
# depends_on:
|
||||
# - postgres-service
|
||||
# - mongo_service
|
||||
# - redis_service
|
||||
# initializer_service:
|
||||
# container_name: initializer_service
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ApiServices/InitialService/Dockerfile
|
||||
# networks:
|
||||
# - wag-services
|
||||
# env_file:
|
||||
# - api_env.env
|
||||
# depends_on:
|
||||
# - postgres-service
|
||||
# - mongo_service
|
||||
# - redis_service
|
||||
|
||||
# template_service:
|
||||
# container_name: template_service
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ApiServices/TemplateService/Dockerfile
|
||||
# networks:
|
||||
# - wag-services
|
||||
# env_file:
|
||||
# - api_env.env
|
||||
# environment:
|
||||
# - API_PATH=app:app
|
||||
# - API_HOST=0.0.0.0
|
||||
# - API_PORT=8000
|
||||
# - API_LOG_LEVEL=info
|
||||
# - API_RELOAD=1
|
||||
# - API_ACCESS_TOKEN_TAG=1
|
||||
# - API_APP_NAME=evyos-template-api-gateway
|
||||
# - API_TITLE=WAG API Template Api Gateway
|
||||
# - API_FORGOT_LINK=https://template_service/forgot-password
|
||||
# - API_DESCRIPTION=This api is serves as web template api gateway only to evyos web services.
|
||||
# - API_APP_URL=https://template_service
|
||||
# ports:
|
||||
# - "8000:8000"
|
||||
# depends_on:
|
||||
# - postgres-service
|
||||
# - mongo_service
|
||||
# - redis_service
|
||||
# template_service:
|
||||
# container_name: template_service
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ApiServices/TemplateService/Dockerfile
|
||||
# networks:
|
||||
# - wag-services
|
||||
# env_file:
|
||||
# - api_env.env
|
||||
# environment:
|
||||
# - API_PATH=app:app
|
||||
# - API_HOST=0.0.0.0
|
||||
# - API_PORT=8000
|
||||
# - API_LOG_LEVEL=info
|
||||
# - API_RELOAD=1
|
||||
# - API_ACCESS_TOKEN_TAG=1
|
||||
# - API_APP_NAME=evyos-template-api-gateway
|
||||
# - API_TITLE=WAG API Template Api Gateway
|
||||
# - API_FORGOT_LINK=https://template_service/forgot-password
|
||||
# - API_DESCRIPTION=This api is serves as web template api gateway only to evyos web services.
|
||||
# - API_APP_URL=https://template_service
|
||||
# ports:
|
||||
# - "8000:8000"
|
||||
# depends_on:
|
||||
# - postgres-service
|
||||
# - mongo_service
|
||||
# - redis_service
|
||||
|
||||
auth_service:
|
||||
container_name: auth_service
|
||||
|
|
@ -161,10 +156,11 @@ services:
|
|||
# networks:
|
||||
# - wag-services
|
||||
|
||||
|
||||
networks:
|
||||
wag-services:
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
mongodb-data:
|
||||
client-frontend:
|
||||
management-frontend:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"name": "prod-wag-backend-automate-services",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"flatpickr": "^4.6.13",
|
||||
"next-crypto": "^1.0.8",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"sonner": "^2.0.3",
|
||||
"zod": "^3.24.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz",
|
||||
"integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==",
|
||||
"dependencies": {
|
||||
"@standard-schema/utils": "^0.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-hook-form": "^7.55.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@standard-schema/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
|
||||
},
|
||||
"node_modules/flatpickr": {
|
||||
"version": "4.6.13",
|
||||
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
|
||||
"integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
|
||||
},
|
||||
"node_modules/next-crypto": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/next-crypto/-/next-crypto-1.0.8.tgz",
|
||||
"integrity": "sha512-6VcrH+xFuuCRGCdDMjFFibhJ97c4s+J/6SEV73RUYJhh38MDW4WXNZNTWIMZBq0B29LOIfAQ0XA37xGUZZCCjA=="
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.55.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.55.0.tgz",
|
||||
"integrity": "sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog==",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/react-hook-form"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/sonner": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz",
|
||||
"integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.24.2",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
|
||||
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"flatpickr": "^4.6.13",
|
||||
"next-crypto": "^1.0.8",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"sonner": "^2.0.3",
|
||||
"zod": "^3.24.2"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue