added pages tested via backend

This commit is contained in:
Berkay 2025-08-06 16:33:03 +03:00
parent 9232da69d3
commit a986ddbb95
33 changed files with 498 additions and 167 deletions

View File

@ -37,7 +37,5 @@ export class AccountsController {
@Post('filter')
@HttpCode(200)
@UseGuards(AuthControlGuard, EndpointControlGuard)
async filterAccounts(@Body() query: any, @Req() req: any) {
return await this.navigator.getFunction(req, this.accountsService.mapper, query)
}
async filterAccounts(@Body() query: any, @Req() req: any) { return await this.navigator.getFunction(req, this.accountsService.mapper, query) }
}

View File

@ -12,9 +12,10 @@ import {
import { SuperUsersService } from './superusers/superusers.service';
import { UrlHandler } from '../utils/navigator/urlHandler';
import { Navigator } from '@/src/utils/navigator/navigator';
import { NavigatorModule } from '../navigator/navigator.module';
@Module({
imports: [PrismaModule, UtilsModule, RedisModule],
imports: [PrismaModule, UtilsModule, RedisModule, NavigatorModule],
providers: [
AccountsService,
AuthControlGuard,

View File

@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { SuperUsersService } from './superusers/superusers.service';
import { EventsService } from '../navigator/events/events.service';
@Injectable()
export class AccountsService {
@ -7,9 +8,32 @@ export class AccountsService {
constructor(
private superUsersService: SuperUsersService,
private eventService: EventsService,
) {
this.mapper = {
"j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": this.superUsersService,
}
}
async onModuleInit() {
Object.entries(this.mapper).map(async ([key, value]) => {
const service = value as any
await this.eventService.setEvents(service.events, "AccountsService")
})
// const accountPages = await fetch(
// "http://localhost:3000/pages",
// {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({
// token: 'j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA',
// pages: {
// }
// })
// }
// )
}
}

View File

@ -235,8 +235,7 @@ export class SelectService {
value: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.value
},
build: {
uu_id: build.uu_id,
build_name: build.build_name
uu_id: build.uu_id, build_name: build.build_name
}
}
},
@ -245,19 +244,9 @@ export class SelectService {
kind: UserType.occupant
});
// Render page and menu
const eventsObject = await this.eventService.getEventsOccupants(livingSpace.uu_id)
eventsObject && (occupantToken.events = eventsObject)
const tokenSelect = await this.redis.setSelectToRedis(
accessToken,
occupantToken,
accessObject.value.users.uu_id,
dto.uuid
);
occupantToken.events = await this.eventService.getEventsOccupants(livingSpace.uu_id) || null
const tokenSelect = await this.redis.setSelectToRedis(accessToken, occupantToken, accessObject.value.users.uu_id, dto.uuid);
return { message: 'Select successful', token: tokenSelect };
} else {
throw new NotAcceptableException('Invalid user type');
}
} else { throw new NotAcceptableException('Invalid user type') }
}
}

View File

@ -1,5 +1,5 @@
import { Injectable, Inject } from '@nestjs/common';
import { Db, Document, Collection, Filter, ObjectId, UpdateResult } from 'mongodb';
import { Db, Document, Collection, Filter, ObjectId, UpdateResult, Sort, SortDirection } from 'mongodb';
@Injectable()
export class MongoService {
@ -12,6 +12,35 @@ export class MongoService {
async getDb() { return this.collection }
async create(data: Record<string, any>): Promise<Document> {
const insertResult = await this.collection.insertOne(data);
if (!insertResult.acknowledged) { throw new Error('Failed to insert document') }
return await this.getOne(insertResult.insertedId);
}
async createMany(data: Record<string, any>[]): Promise<number> {
const insertResult = await this.collection.insertMany(data);
if (!insertResult.acknowledged) { throw new Error('Failed to insert documents') }
return insertResult.insertedCount;
}
async findManyKeyWhere(value: string, limit?: number, skip?: number): Promise<Document[]> {
const docs = await this.collection.find({
$where: function () {
return Object.keys(this).some(key => key.includes(value));
}
}).toArray();
return docs;
}
async findManyKeyWhereValue(value: string, limit?: number, skip?: number): Promise<Document[]> {
const docs = await this.collection.find({
$where: function () {
return Object.keys(this).some(key => this[key] === value);
}
}).toArray();
return docs;
}
/**
* Find a document by UUID or create it if it doesn't exist
* @param data Document data with UUID field
@ -53,10 +82,9 @@ export class MongoService {
* const user = await mongoService.findOne('12345');
* This will search for documents with uuid matching pattern ^TOKEN:12345:
*/
async findOne(filter: Filter<Document>): Promise<Document> {
async findOne(filter: Filter<Document>): Promise<Document | null> {
const result = await this.collection.findOne(filter);
if (!result) { throw new Error(`Document with ID key ${filter} not found`) }
return result;
return result ? result : null;
}
/**
@ -74,11 +102,19 @@ export class MongoService {
* @example
* Find users by role
* const admins = await mongoService.findMany({ role: 'admin' } as Filter<Document>);
* const documents = await mongoService.findMany(filter, limit, skip, ['name', 'createdAt'], ['asc', 'desc']);
*/
async findMany(filter: Filter<Document>, limit?: number, skip?: number): Promise<Document[]> {
async findMany(filter: Filter<Document>, limit?: number, skip?: number, sortBys?: string[], sortDirections?: SortDirection[]): Promise<Document[]> {
let query = this.collection.find(filter);
if (typeof skip === 'number') { query = query.skip(skip) }
if (typeof limit === 'number') { query = query.limit(limit) }
if (sortBys && sortDirections && sortBys.length === sortDirections.length) {
const sortOptions = sortBys.reduce<Record<string, SortDirection>>((acc, sortBy, index) => ({
...acc,
[sortBy]: sortDirections[index]
}), {});
query = query.sort(sortOptions);
}
return await query.toArray();
}
@ -254,4 +290,5 @@ export class MongoService {
return await this.collection.find(query as unknown as Filter<Document>).toArray();
}
}

View File

@ -14,6 +14,8 @@ export class AuthControlGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const accessToken = this.cacheService.mergeLoginKey(req);
if (!accessToken) { throw new ForbiddenException('Send to Login') }
this.cacheService.renewTtlLoginFromRedis(req);
return true;
}
}
@ -32,7 +34,9 @@ export class EndpointControlGuard implements CanActivate {
const keyUrl = `${path}:${method.toUpperCase()}`;
const driveToken = await this.urlHandler.getSecureUrlToken(keyUrl);
const accessObject = await this.cacheService.getSelectFromRedis(req);
if (!accessObject) { throw new ForbiddenException('Access denied') }
req.driveToken = `${driveToken}:${accessObject?.value.functionsRetriever}`;
this.cacheService.renewTtlSelectFromRedis(req);
return true;
}
}

View File

@ -28,3 +28,20 @@ export class mongoGetValidator {
@IsOptional()
skip: number;
}
export class eventSetValidator {
@IsString()
usersUUID: string;
@IsObject()
event: Record<string, Record<string, Record<string, string>>>;
}
export class eventGetValidator {
@IsString()
usersUUID: string;
@IsString()
typeToken: string;
}

View File

@ -6,10 +6,10 @@ import { IsString, IsObject, IsOptional, IsNumber, ValidateNested } from 'class-
export class EventsSetterValidator {
@IsObject()
data: Record<string, Record<string, Record<string, string>>>;
event: Record<string, Record<string, Record<string, string>>>;
@IsString()
dutyUUID: string; // UUID of employee or occupant
userUUID: string; // UUID of employee or occupant
}
export class EventsGetterValidator {

View File

@ -28,6 +28,7 @@ export class EventsService {
seperator = "/"
private async getBuildUUID(uuid: string) {
console.log('uuid', uuid)
const livingSpace = await this.prisma.build_living_space.findFirstOrThrow({
where: { uu_id: uuid },
select: {
@ -97,20 +98,18 @@ export class EventsService {
await this.mongoService.set(`EVENTS${this.seperator}${buildUUID}`);
}
private async findByRegexKey(regexKey: string, searchType: SearchType = 'value'): Promise<Document[]> {
const tokens = await this.mongoService.findByRegexAcrossFields(regexKey, 'i', searchType);
if (tokens.length === 0) {
throw new NotFoundException('Token not found')
}
return tokens;
private escapeRegex(text: string): string {
return text.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
}
private async findByFilter(filter: object): Promise<Document[]> {
const tokens = await this.mongoService.findMany(filter);
if (tokens.length === 0) {
throw new NotFoundException('Token not found')
async getAllEventsOccupants(userTypeToken: string): Promise<Document[] | null> {
await this.mongoService.set(`Events`)
return await this.mongoService.findManyKeyWhere(userTypeToken) || null;
}
return tokens;
async getAllEventsEmployees(userTypeToken: string): Promise<Document[] | null> {
await this.mongoService.set(`Events`)
return await this.mongoService.findManyKeyWhere(userTypeToken) || null;
}
async getEventsOccupants(livingSpaceUUID: string) {
@ -136,51 +135,67 @@ export class EventsService {
return eventsObject
}
async getEventsEmployees(@Body() body: EventsGetterValidator) {
const companyUUID = await this.getCompanyUUID(body.dutyUUID);
async getEventsEmployees(employeeUUID: string) {
const eventsObject = {}
const companyUUID = await this.getCompanyUUID(employeeUUID);
if (!companyUUID) { throw new NotFoundException('Company not found') }
await this.mongoService.set(`EVENTS${this.seperator}${companyUUID}`);
if (body.regexKey) {
// Use the new flattened search for regex with search type
const searchType: SearchType = body.searchType as SearchType || 'value';
const tokens = await this.mongoService.findByRegexAcrossFields(body.regexKey, 'i', searchType);
if (tokens.length === 0) { throw new NotFoundException('Token not found') }
return { data: tokens, message: 'Tokens found' };
} else if (body.filter) {
const tokens = await this.mongoService.findMany(body.filter);
if (tokens.length === 0) { throw new NotFoundException('Token not found') }
return { data: tokens, message: 'Tokens found' };
} else { throw new NotFoundException('Regex key or filter is required') }
const eventsResponse = await this.mongoService.findOne({ [employeeUUID]: { $exists: true } });
if (eventsResponse && typeof eventsResponse === 'object') {
const mapOfEvents = eventsResponse[employeeUUID];
if (mapOfEvents && typeof mapOfEvents === 'object') {
const userTypeTokenKey = Object.keys(mapOfEvents)[0];
const userTypeTokenValue = mapOfEvents[userTypeTokenKey];
if (userTypeTokenValue && typeof userTypeTokenValue === 'object') {
for (const siteUrlTokenKey of Object.keys(userTypeTokenValue)) {
const siteUrlTokenValue = userTypeTokenValue[siteUrlTokenKey];
eventsObject[`${siteUrlTokenKey}:${userTypeTokenKey}`] = siteUrlTokenValue
}
}
}
}
return eventsObject
}
private async setSavedEventToMapper(data: any, useruuid: string) {
await this.mongoService.set(`MAP${this.seperator}EVENTS`);
const events = await this.mongoService.findOrCreate({ uuid: `EVENTS:${useruuid}:${data.uuid}`, data });
const events = await this.mongoService.findOrCreate({ uuid: `EVENTS${this.seperator}${useruuid}:${data.uuid}`, data });
}
private async deleteSavedEventFromMapper(data: any, useruuid: string) {
await this.mongoService.set(`MAP${this.seperator}EVENTS`);
const events = await this.mongoService.deleteMany({ uuid: `EVENTS:${useruuid}:${data.uuid}` });
const events = await this.mongoService.deleteMany({ uuid: `EVENTS${this.seperator}${useruuid}:${data.uuid}` });
return events;
}
async setEventsEmployees(@Body() body: EventsSetterValidator) {
const companyUUID = await this.getCompanyUUID(body.dutyUUID);
const companyUUID = await this.getCompanyUUID(body.userUUID);
if (!companyUUID) { throw new NotFoundException('Company not found') }
await this.mongoService.set(`EVENTS${this.seperator}${companyUUID}`);
const events = await this.mongoService.findOrCreate(body.data);
const events = await this.mongoService.findOrCreate(body.event);
// await this.setSavedEventToMapper(events, body.dutyUUID);
return events;
}
async setEventsOccupants(@Body() body: EventsSetterValidator) {
const buildUUID = await this.getBuildUUID(body.dutyUUID);
const buildUUID = await this.getBuildUUID(body.userUUID);
if (!buildUUID) { throw new NotFoundException('Build not found') }
await this.mongoService.set(`EVENTS${this.seperator}${buildUUID}`);
const events = await this.mongoService.findOrCreate(body.data);
const events = await this.mongoService.findOrCreate(body.event);
return events;
}
async setEvents(events: any, serviceName: string) {
await this.mongoService.set(`Events`);
for (const [key, value] of Object.entries(events)) {
const description = (value as Array<any>)[0].endpoint || "";
console.log(`Setting events for ${serviceName} ${description} is carried to nosql database store.`)
await this.mongoService.deleteMany({ [key]: { $exists: true } });
await this.mongoService.create({ [key]: value });
}
}
async deleteEventsEmployees(@Body() body: EventsGetterValidator) {
const companyUUID = await this.getCompanyUUID(body.dutyUUID);
if (!companyUUID) { throw new NotFoundException('Company not found') }

View File

@ -356,7 +356,7 @@ const menuForOccupantDefinition = [
icon: "",
text: { "tr": "Pano", "en": "Dashboard" },
page: "/dashboard",
token: null,
token: "IbGpchaw3muiY7y9rnV0EJYoPy5XoOOrITT9JlfIbqwE",
color: "",
subs: [],
},

View File

@ -1,25 +1,174 @@
import { Controller, Post, NotFoundException, Body } from '@nestjs/common';
import { PrismaService } from '@/src/prisma.service';
import { EventsService } from '@/src/navigator/events/events.service';
import { MongoService } from '@/src/database/mongo/mongo.service';
import { mongoSetValidator, mongoGetValidator } from '@/src/navigator/dtoValidator';
import { UrlHandler } from '@/src/utils/navigator/urlHandler';
import { eventSetValidator, eventGetValidator } from './dtoValidator';
const tokens = { employeeTypeToken: "L9wBdwV9OlxsLAgh", occupantTypeToken: "j0adQOsJBR0xq24d" }
@Controller('navigator')
export class NavigatorController {
constructor(private mongoService: MongoService) { }
constructor(private prismaService: PrismaService, private eventService: EventsService, private mongoService: MongoService, private urlHandler: UrlHandler) { }
@Post('event/set')
async setEvent(@Body() body: any) { }
async setEvent(@Body() body: eventSetValidator) {
const user = await this.prismaService.users.findFirst({ where: { uu_id: body.usersUUID }, include: { people: true } });
if (!user) { throw new NotFoundException('User not found') }
const userType = await this.prismaService.user_types.findFirstOrThrow({ where: { token: body.event.token } })
const person = user.people[0]
const people2userType = await this.prismaService.employees.findFirstOrThrow({ where: { uu_id: person.uu_id, staff: { user_type_uu_id: userType.uu_id } } })
if (!people2userType) { throw new NotFoundException('User type not found') }
if (userType.type_token == tokens.employeeTypeToken) { await this.eventService.setEventsEmployees({ event: body.event, userUUID: body.usersUUID }) }
else if (userType.type_token == tokens.occupantTypeToken) { await this.eventService.setEventsOccupants({ event: body.event, userUUID: body.usersUUID }) }
else { throw new NotFoundException('User type not found') }
return body.event;
}
@Post('event/get')
async getEvent(@Body() body: any) {
// Get all events from backend statics & Get users registered event from mongo service
async getEvent(@Body() body: eventGetValidator) {
const { typeToken, usersUUID } = body
const userType = await this.prismaService.user_types.findFirstOrThrow({ where: { token: typeToken } })
if (userType.type_token == tokens.employeeTypeToken) {
const allEvents = await this.eventService.getAllEventsEmployees(typeToken);
if (!allEvents) { throw new NotFoundException('Events not found') }
const selectedEvents = await this.eventService.getEventsEmployees(usersUUID);
const selectedEventsKeys = Object.values(selectedEvents).map((value: any) => value.key) || [];
for (const event of allEvents) { if (selectedEventsKeys.includes(event.key)) { event.isSelected = true } else { event.isSelected = false } }
return { events: allEvents }
}
else if (userType.type_token == tokens.occupantTypeToken) {
const allEvents = await this.eventService.getAllEventsOccupants(typeToken);
if (!allEvents) { throw new NotFoundException('Events not found') }
const selectedEvents = await this.eventService.getEventsOccupants(usersUUID);
const selectedEventsKeys = Object.values(selectedEvents).map((value: any) => value.key) || [];
for (const event of allEvents) { if (selectedEventsKeys.includes(event.key)) { event.isSelected = true } else { event.isSelected = false } }
return { events: allEvents }
} else { throw new NotFoundException('User type not found') }
}
@Post('page/set')
async setPage(@Body() body: any) { }
async setPage(@Body() body: { usersUUID: string, usersToken: string, url: string, page: Record<string, any> }) {
const user = await this.prismaService.users.findFirstOrThrow({ where: { uu_id: body.usersUUID }, include: { people: true } });
const userType = await this.prismaService.user_types.findFirstOrThrow({ where: { token: body.usersToken } })
const urlToken = await this.urlHandler.getSecureUrlToken(body.url)
if (userType.type_token == tokens.employeeTypeToken) {
const person = user.people[0]
const employee = await this.prismaService.employees.findFirstOrThrow({ where: { people_id: person.id, staff: { user_type_id: userType.id } } })
const companyUUID = await this.prismaService.companies.findFirstOrThrow({
where: { departments: { some: { duties: { some: { staff: { some: { uu_id: employee.staff_uu_id } } } } } } }, select: { uu_id: true }
})
this.mongoService.set(`Pages/${companyUUID.uu_id}`);
const userPage = await this.mongoService.findOne({ [employee.uu_id]: { $exists: true } });
if (!userPage) {
console.log('urlToken', urlToken)
} else { console.log('urlToken', urlToken) }
} else if (userType.type_token == tokens.occupantTypeToken) {
const person = user.people
const livingSpace = await this.prismaService.build_living_space.findFirstOrThrow({
where: { person_id: person.id, occupant_types: { user_types: { id: userType.id } } },
select: { uu_id: true, build_parts_id: true }
})
const buildUUID = await this.prismaService.build.findFirstOrThrow({
where: { build_parts: { some: { id: livingSpace.build_parts_id } } }, select: { uu_id: true }
})
this.mongoService.set(`Pages/${buildUUID.uu_id}`);
const userPage = await this.mongoService.findOne({ [user.uu_id]: { $exists: true } });
if (!userPage) {
const newUserPageSlot = await this.mongoService.create({ [user.uu_id]: { [`${body.usersToken}`]: { [`${urlToken}`]: `${body.page.key}` } } })
return newUserPageSlot
} else {
console.log('body')
console.dir(body, { depth: null })
console.log('userPage')
console.dir(userPage, { depth: null })
const updatedUserPageSlot = await this.mongoService.updateOne(
userPage._id, { [`${user.uu_id}.${body.usersToken}.${urlToken}`]: `${body.page.key}` }
)
return updatedUserPageSlot ? {
status: "success",
data: updatedUserPageSlot
} : {
status: "error",
data: null
}
}
// console.log('urlToken', { [user.uu_id]: { [`${body.usersToken}`]: { [`${urlToken}`]: `${body.page.key}` } } })
}
else { throw new NotFoundException('User type not found') }
}
@Post('page/get')
async getPage(@Body() body: any) {
// Get all pages from Frontend & Get users registered page from mongo service
async getPage(@Body() body: { usersUUID: string, token: string, url?: string, skip?: number, limit?: number }) {
this.mongoService.set("Pages");
const addUrlQuery = body.url ? { url: body.url } : {};
const user = await this.prismaService.users.findFirstOrThrow({ where: { uu_id: body.usersUUID }, include: { people: true } });
const userType = await this.prismaService.user_types.findFirstOrThrow({ where: { token: body.token } })
if (userType.type_token == tokens.employeeTypeToken) {
const person = user.people[0]
const pages = await this.mongoService.findMany({
$and: [
{ $or: [{ includeTokens: { $in: ['*'] } }, { includeTokens: { $in: [body.token] } }] },
{ $nor: [{ excludeTokens: { $in: ['*'] } }, { excludeTokens: { $in: [body.token] } }] },
addUrlQuery,
{ typeToken: tokens.employeeTypeToken },
],
}, body.limit || 50, body.skip || 0, ['url'], ['asc'])
if (!pages) { throw new NotFoundException(`Pages not found. User type: ${userType.type_token}`) }
const employee = await this.prismaService.employees.findFirstOrThrow({
where: { people_id: person.id, staff: { user_type_id: userType.id } },
select: { uu_id: true, staff_uu_id: true }
})
const companyUUID = await this.prismaService.companies.findFirstOrThrow({
where: { departments: { some: { duties: { some: { staff: { some: { uu_id: employee.staff_uu_id } } } } } } }, select: { uu_id: true }
})
this.mongoService.set(`Pages/${companyUUID.uu_id}`);
const usersPages = await this.mongoService.findMany({ [employee.uu_id]: { $exists: true } });
return pages;
}
else if (userType.type_token == tokens.occupantTypeToken) {
const person = user.people
const pages = await this.mongoService.findMany({
$and: [
{ $or: [{ includeTokens: { $in: ['*'] } }, { includeTokens: { $in: [body.token] } }] },
{ $nor: [{ excludeTokens: { $in: ['*'] } }, { excludeTokens: { $in: [body.token] } }] },
addUrlQuery,
{ typeToken: tokens.occupantTypeToken },
],
}, body.limit || 50, body.skip || 0, ['url'], ['asc'])
console.log('pages', pages)
if (!pages) { throw new NotFoundException(`Pages not found. User type: ${userType.type_token}`) }
const livingSpace = await this.prismaService.build_living_space.findFirstOrThrow({
where: { person_id: person.id, occupant_types: { user_types: { id: userType.id } } },
select: { uu_id: true, build_parts_id: true }
})
console.log('livingSpace', livingSpace)
const buildUUID = await this.prismaService.build.findFirstOrThrow({
where: { build_parts: { some: { id: livingSpace.build_parts_id } } }, select: { uu_id: true }
})
this.mongoService.set(`Pages/${buildUUID.uu_id}`);
const usersPages = await this.mongoService.findMany({ [livingSpace.uu_id]: { $exists: true } });
console.log('usersPages', usersPages)
return Object.entries(pages).map(([key, value]: [string, any]) => {
if (usersPages.some((page: any) => page[key])) { value.isSelected = true } else { value.isSelected = false }
return value;
})
}
else { throw new NotFoundException('User type not found') }
}
private async saveChunkToDB(data: Record<string, any>, chunkIndex: number) {
await this.mongoService.set("Pages");
if (chunkIndex == 1) { await this.mongoService.deleteMany({}) }
await this.mongoService.createMany(Object.values(data));
}
@Post('page/configure')
async setPages(@Body() body: { chunkIndex: number; chunkCount: number; data: Record<string, any> }) {
const count = Object.keys(body.data).length;
console.log(`🧩 Chunk [${body.chunkIndex}/${body.chunkCount}] alındı. Kayıt sayısı: ${count}`);
await this.saveChunkToDB(body.data, body.chunkIndex);
return { message: 'Chunk işlendi', count };
}
}

View File

@ -4,11 +4,12 @@ import { MenusService } from '@/src/navigator/menus/services.service';
import { NavigatorController } from '@/src/navigator/navigator.controller';
import { EventsService } from './events/events.service';
import { PrismaService } from '@/src/prisma.service';
import { UrlHandler } from '@/src/utils/navigator/urlHandler';
@Module({
controllers: [NavigatorController],
imports: [MongoModule],
providers: [MenusService, EventsService, PrismaService],
providers: [MenusService, EventsService, PrismaService, UrlHandler],
exports: [MenusService, EventsService, PrismaService]
})
export class NavigatorModule {

View File

@ -6,6 +6,18 @@ export class Navigator {
constructor(private redisHandler: RedisHandlers) { }
async getAllInfos(mainService: any) {
const mainServiceMapper = mainService?.mapper
if (!mainServiceMapper) { throw new ForbiddenException(`Mapper in ${mainService.constructor.name} is missing or null`) }
const allInfos = Object.entries(mainServiceMapper).map(([key, value]) => {
return {
key,
value
}
})
return allInfos
}
async getInfos(mainService: any, userToken: string) {
// Get asked service by userToken
const mainServiceMapper = mainService?.mapper

View File

@ -5,7 +5,7 @@ import { createHash } from 'crypto';
@Injectable()
export class UrlHandler {
private createSecureKeyWithoutLib(url: string): string {
const subString = createHash('sha256').update(url).digest().toString('base64').substring(0, 16)
const subString = createHash('sha256').update(url).digest().toString('base64').substring(0, 48)
return subString.replace(/=/g, 'E').replace(/-/g, 'M').replace(/_/g, 'N').replace(/\+/g, 'P').replace(/\//g, 'Q')
}
@ -13,7 +13,6 @@ export class UrlHandler {
return this.createSecureKeyWithoutLib(url);
}
async getEvents(events: Events, mapper: Mapper) {
for (const keyUrl of Object.keys(mapper)) {
const splittedMapper = keyUrl.split(':')

View File

@ -224,15 +224,15 @@ export class RedisHandlers {
}
async renewTtlLoginFromRedis(req: Request): Promise<any> {
const mergedKey = this.mergeLoginKey(req);
const value = await this.cacheService.get(mergedKey);
return this.cacheService.set_with_ttl(mergedKey, value, 86400);
const loginToken = await this.getLoginFromRedis(req);
if (!loginToken) { throw new ForbiddenException('Login token not found') }
return this.cacheService.set_with_ttl(loginToken.key, loginToken.value, 60 * 30);
}
async renewTtlSelectFromRedis(req: Request): Promise<any> {
const mergedKey = this.mergeSelectKey(req);
const value = await this.cacheService.get(mergedKey);
return this.cacheService.set_with_ttl(mergedKey, value, 60 * 30);
const selectToken = await this.getSelectFromRedis(req);
if (!selectToken) { throw new ForbiddenException('Select token not found') }
return this.cacheService.set_with_ttl(selectToken.key, selectToken.value, 60 * 30);
}
async setLoginToRedis(token: AuthToken, userUUID: string): Promise<any> {

View File

@ -18,6 +18,7 @@
"next-intl": "^4.3.4",
"react": "19.1.0",
"react-dom": "19.1.0",
"undici": "^7.13.0",
"zod": "^4.0.10"
},
"devDependencies": {
@ -2318,6 +2319,15 @@
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.13.0.tgz",
"integrity": "sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",

View File

@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"reset-sync": "rm ./.next/server/app/[locale]/sync.lock"
},
"dependencies": {
"clsx": "^2.1.1",
@ -19,6 +20,7 @@
"next-intl": "^4.3.4",
"react": "19.1.0",
"react-dom": "19.1.0",
"undici": "^7.13.0",
"zod": "^4.0.10"
},
"devDependencies": {

View File

@ -8,9 +8,9 @@ import { getSelectToken } from '@/fetchers/token/select';
export default async function DashboardPage() {
const pageUrl = "/office/dashboard";
const selectToken = await getSelectToken();
try {
const RenderPage = renderPage(selectToken, pageUrl, dashboardPages);
if (RenderPage) {
return <>
<div>Dashboard Page</div>
<div className='flex align-center justify-center h-screen w-screen mt-10 text-2xl'>
@ -18,6 +18,7 @@ export default async function DashboardPage() {
</div>
</>
}
} catch (error) { console.log(error) }
return <>
<div>Dashboard Page</div>
<div>You are not allowed to reach any page under {pageUrl}. Please contact your administrator.</div>

View File

@ -1,9 +1,8 @@
import { ReactNode } from 'react';
import { Inter } from 'next/font/google';
import { notFound } from "next/navigation";
import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { Locale, locales } from "@/i18n/locales";
import { routing } from "@/i18n/routing";
import { NextIntlClientProvider } from 'next-intl';
import '../globals.css';
@ -19,41 +18,26 @@ export function generateStaticParams() {
}
export async function generateMetadata({ params }: Omit<Props, 'children'>) {
// Properly await params before accessing properties
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'LocaleLayout' });
return {
title: t('title')
};
return { title: t('title') };
}
export default async function LocaleLayout({ children, params }: Props) {
// Properly await params before accessing properties
const { locale } = await params;
// Validate that the incoming locale is valid
if (!locales.includes(locale as Locale)) {
notFound();
redirect('/' + locales[0]);
}
// Load messages for all child components
const messages = (await import(`@/i18n/${locale}.json`)).default;
// Enable static rendering
// Note: unstable_setRequestLocale is removed as it's causing TypeScript errors
return (
<>
<html lang={locale}>
<body className={inter.className}>
<NextIntlClientProvider
locale={locale}
messages={messages}
timeZone="Europe/Istanbul" // Configure a default timezone for Turkey
>
<NextIntlClientProvider locale={locale} messages={messages} timeZone="Europe/Istanbul">
{children}
</NextIntlClientProvider>
</body>
</html>
</>
);
}

View File

@ -1,6 +1,8 @@
'use server';
import HomePage from '@/app/home-page';
import { sendChunksToNest } from '@/lib/init-sync';
export default async function Home() {
sendChunksToNest();
return <HomePage />;
}

View File

@ -0,0 +1,5 @@
import { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return children;
}

View File

@ -0,0 +1,8 @@
'use server'
import { redirect } from '@/i18n/navigation';
const RedirectHome = async () => {
return redirect({ locale: "en", href: "/" })
}
export default RedirectHome

View File

@ -0,0 +1,58 @@
import fs from 'fs';
import path from 'path';
import { employeeMapper } from '@/pages/office/mapper';
import { occupantMapper } from '@/pages/venue/mapper';
import { Page } from '@/pages/types/page';
function chunkifyObject<T>(obj: Record<string, T>, chunkSize: number): Record<string, T>[] {
const keys = Object.keys(obj);
const chunks: Record<string, T>[] = [];
for (let i = 0; i < keys.length; i += chunkSize) {
const chunk: Record<string, T> = {};
keys.slice(i, i + chunkSize).forEach((key) => {
chunk[key] = obj[key];
});
chunks.push(chunk);
}
return chunks;
}
export async function sendChunksToNest() {
const sendObject: Page = { ...employeeMapper, ...occupantMapper };
const lockPath = path.join(__dirname, 'sync.lock');
if (fs.existsSync(lockPath)) {
console.log('🔁 Zaten sync edilmiş, işlem atlandı.');
return;
}
const chunks = chunkifyObject(sendObject, 50);
const totalChunks = chunks.length;
for (let i = 0; i < totalChunks; i++) {
const chunk = chunks[i];
// 👇 Bu şekilde index bilgisiyle beraber nested body oluşturuyoruz
const payload = {
chunkIndex: i + 1,
chunkCount: totalChunks,
data: chunk,
};
try {
const response = await fetch('http://localhost:8001/navigator/page/configure', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const result = await response.json();
console.log(`✅ [${payload.chunkIndex}/${payload.chunkCount}] Chunk gönderildi. Kayıt sayısı: ${result.count}`);
} catch (err) {
console.error(`❌ [${i + 1}/${totalChunks}] Chunk gönderimi sırasında hata:`, err);
}
}
fs.writeFileSync(lockPath, 'done');
console.log('🎉 Tüm chunklar gönderildi ve lock dosyası oluşturuldu.');
}

View File

@ -1,8 +1,8 @@
function renderPage(selectToken: any, pageUrl: string, dashboardPages: any) {
function renderPage(selectToken: any, pageUrl: string, fromTokenPages: any) {
const subPageKey = selectToken.pages[pageUrl];
if (Object.keys(dashboardPages).includes(subPageKey)) {
const subPage = dashboardPages[subPageKey as keyof typeof dashboardPages];
if (Object.keys(fromTokenPages).includes(subPageKey)) {
const subPage = fromTokenPages[subPageKey as keyof typeof fromTokenPages];
if (subPage) { if (subPage.page) { return subPage.page } }
} return null;
}

View File

@ -0,0 +1,11 @@
'use client';
const DashboardPtnZblJTri0DnlQaUOikQx: React.FC = () => {
return (
<div>
<h1>DashboardPtnZblJTri0DnlQaUOikQx</h1>
</div>
);
}
export default DashboardPtnZblJTri0DnlQaUOikQx;

View File

@ -1,11 +0,0 @@
'use client';
const DashboardU0QncONSk22PFxZ5xefmgx: React.FC = () => {
return (
<div>
<h1>DashboardU0QncONSk22PFxZ5xefmgx</h1>
</div>
);
}
export default DashboardU0QncONSk22PFxZ5xefmgx;

View File

@ -1,17 +1,19 @@
import { Page } from "@/pages/types/page";
import DashboardU0QncONSk22PFxZ5xefmgx from "./U0QncONSk22PFxZ5xefmgx";
import DashboardPtnZblJTri0DnlQaUOikQx from "./PtnZblJTri0DnlQaUOikQx";
const dashboardPages: Page = {
"qY56XMEr08wJkNvOR6EYQZKMVdTQEfHdLXGzzxcKU24E:U0QncONSk22PFxZ5xefmgx": {
name: "DashboardU0QncONSk22PFxZ5xefmgx",
key: "U0QncONSk22PFxZ5xefmgx",
url: "/venue/dashboard",
page: DashboardU0QncONSk22PFxZ5xefmgx,
"qY56XMEr08wJkNvOR6EYQZKMVdTQEfHdLXGzzxcKU24E:PtnZblJTri0DnlQaUOikQx": {
name: "DashboardPtnZblJTri0DnlQaUOikQx",
key: "PtnZblJTri0DnlQaUOikQx",
url: "/office/dashboard",
page: DashboardPtnZblJTri0DnlQaUOikQx,
description: "Dashboard",
isDefault: true,
typeToken: "L9wBdwV9OlxsLAgh",
params: {},
events: ["Aevent", "Aevent", "Aevent"],
tokens: ["TOKEN", "TOKEN", "TOKEN", "TOKEN"]
includeTokens: ['*'],
excludeTokens: []
}
};

View File

@ -0,0 +1,4 @@
import {dashboardPages} from "./dashboard/mapper";
export const employeeMapper = {
...dashboardPages
}

View File

@ -6,9 +6,11 @@ export interface Page {
page: React.FC;
description: string;
isDefault: boolean;
typeToken: string;
params: Record<string, boolean>;
events?: string[];
tokens?: string[];
includeTokens?: string[];
excludeTokens?: string[];
}
}

View File

@ -1,11 +0,0 @@
'use client';
const DashboardIdTch3qS9aJXkvqXodAxxx: React.FC = () => {
return (
<div>
<h1>DashboardIdTch3qS9aJXkvqXodAxxx</h1>
</div>
);
}
export default DashboardIdTch3qS9aJXkvqXodAxxx;

View File

@ -0,0 +1,11 @@
'use client';
const DashboardhES1KfaPRZeadmmjdryShA: React.FC = () => {
return (
<div>
<h1>DashboardhES1KfaPRZeadmmjdryShA</h1>
</div>
);
}
export default DashboardhES1KfaPRZeadmmjdryShA;

View File

@ -1,17 +1,19 @@
import { Page } from "@/pages/types/page";
import DashboardIdTch3qS9aJXkvqXodAxxx from "./IdTch3qS9aJXkvqXodAxxx";
import DashboardhES1KfaPRZeadmmjdryShA from "./hES1KfaPRZeadmmjdryShA";
const dashboardPages: Page = {
"IbGpchaw3muiY7y9rnV0EJYoPy5XoOOrITT9JlfIbqwE:IdTch3qS9aJXkvqXodAxxx": {
name: "DashboardIdTch3qS9aJXkvqXodAxxx",
key: "IdTch3qS9aJXkvqXodAxxx",
"IbGpchaw3muiY7y9rnV0EJYoPy5XoOOrITT9JlfIbqwE:hES1KfaPRZeadmmjdryShA": {
name: "DashboardhES1KfaPRZeadmmjdryShA",
key: "hES1KfaPRZeadmmjdryShA",
url: "/venue/dashboard",
page: DashboardIdTch3qS9aJXkvqXodAxxx,
page: DashboardhES1KfaPRZeadmmjdryShA,
description: "Dashboard",
isDefault: true,
typeToken: "j0adQOsJBR0xq24d",
params: {},
events: ["Aevent", "Aevent", "Aevent"],
tokens: ["TOKEN", "TOKEN", "TOKEN", "TOKEN"]
events: [],
includeTokens: ["*"],
excludeTokens: []
}
};

View File

@ -0,0 +1,5 @@
import { dashboardPages } from "./dashboard/mapper";
export const occupantMapper = {
...dashboardPages,
}