Neo platform - First Version

This commit is contained in:
Francisco Gaona
2025-11-25 12:21:14 +01:00
commit 484af68571
59 changed files with 3699 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { AppBuilderService } from './app-builder.service';
import { RuntimeAppController } from './runtime-app.controller';
import { SetupAppController } from './setup-app.controller';
@Module({
providers: [AppBuilderService],
controllers: [RuntimeAppController, SetupAppController],
exports: [AppBuilderService],
})
export class AppBuilderModule {}

View File

@@ -0,0 +1,242 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class AppBuilderService {
constructor(private prisma: PrismaService) {}
// Runtime endpoints
async getApps(tenantId: string, userId: string) {
// For now, return all active apps for the tenant
// In production, you'd filter by user permissions
return this.prisma.app.findMany({
where: {
tenantId,
isActive: true,
},
include: {
pages: {
where: { isActive: true },
orderBy: { sortOrder: 'asc' },
},
},
orderBy: { label: 'asc' },
});
}
async getApp(tenantId: string, slug: string, userId: string) {
const app = await this.prisma.app.findUnique({
where: {
tenantId_slug: {
tenantId,
slug,
},
},
include: {
pages: {
where: { isActive: true },
orderBy: { sortOrder: 'asc' },
},
},
});
if (!app) {
throw new NotFoundException(`App ${slug} not found`);
}
return app;
}
async getPage(
tenantId: string,
appSlug: string,
pageSlug: string,
userId: string,
) {
const app = await this.getApp(tenantId, appSlug, userId);
const page = await this.prisma.appPage.findFirst({
where: {
appId: app.id,
slug: pageSlug,
isActive: true,
},
include: {
object: {
include: {
fields: {
where: { isActive: true },
},
},
},
},
});
if (!page) {
throw new NotFoundException(`Page ${pageSlug} not found`);
}
return page;
}
// Setup endpoints
async getAllApps(tenantId: string) {
return this.prisma.app.findMany({
where: { tenantId },
include: {
pages: {
orderBy: { sortOrder: 'asc' },
},
},
orderBy: { label: 'asc' },
});
}
async getAppForSetup(tenantId: string, slug: string) {
const app = await this.prisma.app.findUnique({
where: {
tenantId_slug: {
tenantId,
slug,
},
},
include: {
pages: {
orderBy: { sortOrder: 'asc' },
},
},
});
if (!app) {
throw new NotFoundException(`App ${slug} not found`);
}
return app;
}
async createApp(
tenantId: string,
data: {
slug: string;
label: string;
description?: string;
icon?: string;
},
) {
return this.prisma.app.create({
data: {
tenantId,
...data,
},
});
}
async updateApp(
tenantId: string,
slug: string,
data: {
label?: string;
description?: string;
icon?: string;
isActive?: boolean;
},
) {
const app = await this.getAppForSetup(tenantId, slug);
return this.prisma.app.update({
where: { id: app.id },
data,
});
}
async createPage(
tenantId: string,
appSlug: string,
data: {
slug: string;
label: string;
type: string;
objectApiName?: string;
config?: any;
sortOrder?: number;
},
) {
const app = await this.getAppForSetup(tenantId, appSlug);
// If objectApiName is provided, find the object
let objectId: string | undefined;
if (data.objectApiName) {
const obj = await this.prisma.objectDefinition.findUnique({
where: {
tenantId_apiName: {
tenantId,
apiName: data.objectApiName,
},
},
});
objectId = obj?.id;
}
return this.prisma.appPage.create({
data: {
appId: app.id,
slug: data.slug,
label: data.label,
type: data.type,
objectApiName: data.objectApiName,
objectId,
config: data.config,
sortOrder: data.sortOrder || 0,
},
});
}
async updatePage(
tenantId: string,
appSlug: string,
pageSlug: string,
data: {
label?: string;
type?: string;
objectApiName?: string;
config?: any;
sortOrder?: number;
isActive?: boolean;
},
) {
const app = await this.getAppForSetup(tenantId, appSlug);
const page = await this.prisma.appPage.findFirst({
where: {
appId: app.id,
slug: pageSlug,
},
});
if (!page) {
throw new NotFoundException(`Page ${pageSlug} not found`);
}
// If objectApiName is provided, find the object
let objectId: string | undefined;
if (data.objectApiName) {
const obj = await this.prisma.objectDefinition.findUnique({
where: {
tenantId_apiName: {
tenantId,
apiName: data.objectApiName,
},
},
});
objectId = obj?.id;
}
return this.prisma.appPage.update({
where: { id: page.id },
data: {
...data,
objectId,
},
});
}
}

View File

@@ -0,0 +1,45 @@
import {
Controller,
Get,
Param,
UseGuards,
} from '@nestjs/common';
import { AppBuilderService } from './app-builder.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CurrentUser } from '../auth/current-user.decorator';
import { TenantId } from '../tenant/tenant.decorator';
@Controller('runtime/apps')
@UseGuards(JwtAuthGuard)
export class RuntimeAppController {
constructor(private appBuilderService: AppBuilderService) {}
@Get()
async getApps(@TenantId() tenantId: string, @CurrentUser() user: any) {
return this.appBuilderService.getApps(tenantId, user.userId);
}
@Get(':appSlug')
async getApp(
@TenantId() tenantId: string,
@Param('appSlug') appSlug: string,
@CurrentUser() user: any,
) {
return this.appBuilderService.getApp(tenantId, appSlug, user.userId);
}
@Get(':appSlug/pages/:pageSlug')
async getPage(
@TenantId() tenantId: string,
@Param('appSlug') appSlug: string,
@Param('pageSlug') pageSlug: string,
@CurrentUser() user: any,
) {
return this.appBuilderService.getPage(
tenantId,
appSlug,
pageSlug,
user.userId,
);
}
}

View File

@@ -0,0 +1,69 @@
import {
Controller,
Get,
Post,
Put,
Param,
Body,
UseGuards,
} from '@nestjs/common';
import { AppBuilderService } from './app-builder.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { TenantId } from '../tenant/tenant.decorator';
@Controller('setup/apps')
@UseGuards(JwtAuthGuard)
export class SetupAppController {
constructor(private appBuilderService: AppBuilderService) {}
@Get()
async getAllApps(@TenantId() tenantId: string) {
return this.appBuilderService.getAllApps(tenantId);
}
@Get(':appSlug')
async getApp(
@TenantId() tenantId: string,
@Param('appSlug') appSlug: string,
) {
return this.appBuilderService.getAppForSetup(tenantId, appSlug);
}
@Post()
async createApp(@TenantId() tenantId: string, @Body() data: any) {
return this.appBuilderService.createApp(tenantId, data);
}
@Put(':appSlug')
async updateApp(
@TenantId() tenantId: string,
@Param('appSlug') appSlug: string,
@Body() data: any,
) {
return this.appBuilderService.updateApp(tenantId, appSlug, data);
}
@Post(':appSlug/pages')
async createPage(
@TenantId() tenantId: string,
@Param('appSlug') appSlug: string,
@Body() data: any,
) {
return this.appBuilderService.createPage(tenantId, appSlug, data);
}
@Put(':appSlug/pages/:pageSlug')
async updatePage(
@TenantId() tenantId: string,
@Param('appSlug') appSlug: string,
@Param('pageSlug') pageSlug: string,
@Body() data: any,
) {
return this.appBuilderService.updatePage(
tenantId,
appSlug,
pageSlug,
data,
);
}
}