updated Service providers

This commit is contained in:
2025-04-05 22:54:05 +03:00
parent fa4df11323
commit 60507c87e0
21 changed files with 1134 additions and 183 deletions

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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>
);
}

View File

@@ -0,0 +1,7 @@
import React from "react";
function SelectPage() {
return <div>SelectPage</div>;
}
export default SelectPage;

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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;