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 { ObjectService } from './object.service';
import { RuntimeObjectController } from './runtime-object.controller';
import { SetupObjectController } from './setup-object.controller';
@Module({
providers: [ObjectService],
controllers: [RuntimeObjectController, SetupObjectController],
exports: [ObjectService],
})
export class ObjectModule {}

View File

@@ -0,0 +1,191 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class ObjectService {
constructor(private prisma: PrismaService) {}
// Setup endpoints - Object metadata management
async getObjectDefinitions(tenantId: string) {
return this.prisma.objectDefinition.findMany({
where: { tenantId },
include: {
fields: true,
},
orderBy: { label: 'asc' },
});
}
async getObjectDefinition(tenantId: string, apiName: string) {
const obj = await this.prisma.objectDefinition.findUnique({
where: {
tenantId_apiName: {
tenantId,
apiName,
},
},
include: {
fields: {
where: { isActive: true },
orderBy: { label: 'asc' },
},
},
});
if (!obj) {
throw new NotFoundException(`Object ${apiName} not found`);
}
return obj;
}
async createObjectDefinition(
tenantId: string,
data: {
apiName: string;
label: string;
pluralLabel?: string;
description?: string;
isSystem?: boolean;
},
) {
return this.prisma.objectDefinition.create({
data: {
tenantId,
...data,
tableName: `custom_${data.apiName.toLowerCase()}`,
},
});
}
async createFieldDefinition(
tenantId: string,
objectApiName: string,
data: {
apiName: string;
label: string;
type: string;
description?: string;
isRequired?: boolean;
isUnique?: boolean;
isLookup?: boolean;
referenceTo?: string;
defaultValue?: string;
options?: any;
},
) {
const obj = await this.getObjectDefinition(tenantId, objectApiName);
return this.prisma.fieldDefinition.create({
data: {
objectId: obj.id,
...data,
},
});
}
// Runtime endpoints - CRUD operations
async getRecords(
tenantId: string,
objectApiName: string,
userId: string,
filters?: any,
) {
// For demonstration, using Account as example static object
if (objectApiName === 'Account') {
return this.prisma.account.findMany({
where: {
tenantId,
ownerId: userId, // Basic sharing rule
...filters,
},
});
}
// For custom objects, you'd need dynamic query building
// This is a simplified version
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
}
async getRecord(
tenantId: string,
objectApiName: string,
recordId: string,
userId: string,
) {
if (objectApiName === 'Account') {
const record = await this.prisma.account.findFirst({
where: {
id: recordId,
tenantId,
ownerId: userId,
},
});
if (!record) {
throw new NotFoundException('Record not found');
}
return record;
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
}
async createRecord(
tenantId: string,
objectApiName: string,
data: any,
userId: string,
) {
if (objectApiName === 'Account') {
return this.prisma.account.create({
data: {
tenantId,
ownerId: userId,
...data,
},
});
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
}
async updateRecord(
tenantId: string,
objectApiName: string,
recordId: string,
data: any,
userId: string,
) {
if (objectApiName === 'Account') {
// Verify ownership
await this.getRecord(tenantId, objectApiName, recordId, userId);
return this.prisma.account.update({
where: { id: recordId },
data,
});
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
}
async deleteRecord(
tenantId: string,
objectApiName: string,
recordId: string,
userId: string,
) {
if (objectApiName === 'Account') {
// Verify ownership
await this.getRecord(tenantId, objectApiName, recordId, userId);
return this.prisma.account.delete({
where: { id: recordId },
});
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
}
}

View File

@@ -0,0 +1,98 @@
import {
Controller,
Get,
Post,
Put,
Delete,
Param,
Body,
Query,
UseGuards,
} from '@nestjs/common';
import { ObjectService } from './object.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CurrentUser } from '../auth/current-user.decorator';
import { TenantId } from '../tenant/tenant.decorator';
@Controller('runtime/objects')
@UseGuards(JwtAuthGuard)
export class RuntimeObjectController {
constructor(private objectService: ObjectService) {}
@Get(':objectApiName/records')
async getRecords(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
@CurrentUser() user: any,
@Query() query: any,
) {
return this.objectService.getRecords(
tenantId,
objectApiName,
user.userId,
query,
);
}
@Get(':objectApiName/records/:id')
async getRecord(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
@Param('id') id: string,
@CurrentUser() user: any,
) {
return this.objectService.getRecord(
tenantId,
objectApiName,
id,
user.userId,
);
}
@Post(':objectApiName/records')
async createRecord(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
@Body() data: any,
@CurrentUser() user: any,
) {
return this.objectService.createRecord(
tenantId,
objectApiName,
data,
user.userId,
);
}
@Put(':objectApiName/records/:id')
async updateRecord(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
@Param('id') id: string,
@Body() data: any,
@CurrentUser() user: any,
) {
return this.objectService.updateRecord(
tenantId,
objectApiName,
id,
data,
user.userId,
);
}
@Delete(':objectApiName/records/:id')
async deleteRecord(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
@Param('id') id: string,
@CurrentUser() user: any,
) {
return this.objectService.deleteRecord(
tenantId,
objectApiName,
id,
user.userId,
);
}
}

View File

@@ -0,0 +1,51 @@
import {
Controller,
Get,
Post,
Param,
Body,
UseGuards,
} from '@nestjs/common';
import { ObjectService } from './object.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { TenantId } from '../tenant/tenant.decorator';
@Controller('setup/objects')
@UseGuards(JwtAuthGuard)
export class SetupObjectController {
constructor(private objectService: ObjectService) {}
@Get()
async getObjectDefinitions(@TenantId() tenantId: string) {
return this.objectService.getObjectDefinitions(tenantId);
}
@Get(':objectApiName')
async getObjectDefinition(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
) {
return this.objectService.getObjectDefinition(tenantId, objectApiName);
}
@Post()
async createObjectDefinition(
@TenantId() tenantId: string,
@Body() data: any,
) {
return this.objectService.createObjectDefinition(tenantId, data);
}
@Post(':objectApiName/fields')
async createFieldDefinition(
@TenantId() tenantId: string,
@Param('objectApiName') objectApiName: string,
@Body() data: any,
) {
return this.objectService.createFieldDefinition(
tenantId,
objectApiName,
data,
);
}
}