WIP - additional fixes for multitenant
This commit is contained in:
@@ -5,28 +5,28 @@ exports.up = function (knex) {
|
||||
table.string('slug', 255).notNullable().unique();
|
||||
table.string('label', 255).notNullable();
|
||||
table.text('description');
|
||||
table.integer('displayOrder').defaultTo(0);
|
||||
table.integer('display_order').defaultTo(0);
|
||||
table.timestamps(true, true);
|
||||
|
||||
table.index(['slug']);
|
||||
})
|
||||
.createTable('app_pages', (table) => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('(UUID())'));
|
||||
table.uuid('appId').notNullable();
|
||||
table.uuid('app_id').notNullable();
|
||||
table.string('slug', 255).notNullable();
|
||||
table.string('label', 255).notNullable();
|
||||
table.string('type', 50).notNullable(); // List, Detail, Custom
|
||||
table.string('objectApiName', 255);
|
||||
table.integer('displayOrder').defaultTo(0);
|
||||
table.string('object_api_name', 255);
|
||||
table.integer('display_order').defaultTo(0);
|
||||
table.timestamps(true, true);
|
||||
|
||||
table
|
||||
.foreign('appId')
|
||||
.foreign('app_id')
|
||||
.references('id')
|
||||
.inTable('apps')
|
||||
.onDelete('CASCADE');
|
||||
table.unique(['appId', 'slug']);
|
||||
table.index(['appId']);
|
||||
table.unique(['app_id', 'slug']);
|
||||
table.index(['app_id']);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `isActive` on the `tenants` table. All the data in the column will be lost.
|
||||
- You are about to drop the `accounts` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `app_pages` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `apps` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `field_definitions` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `object_definitions` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `permissions` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `role_permissions` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `roles` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `user_roles` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `users` table. If the table is not empty, all the data it contains will be lost.
|
||||
- Added the required column `dbHost` to the `tenants` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `dbName` to the `tenants` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `dbPassword` to the `tenants` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `dbUsername` to the `tenants` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `accounts` DROP FOREIGN KEY `accounts_ownerId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `accounts` DROP FOREIGN KEY `accounts_tenantId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `app_pages` DROP FOREIGN KEY `app_pages_appId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `app_pages` DROP FOREIGN KEY `app_pages_objectId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `apps` DROP FOREIGN KEY `apps_tenantId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `field_definitions` DROP FOREIGN KEY `field_definitions_objectId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `object_definitions` DROP FOREIGN KEY `object_definitions_tenantId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `permissions` DROP FOREIGN KEY `permissions_tenantId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `role_permissions` DROP FOREIGN KEY `role_permissions_permissionId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `role_permissions` DROP FOREIGN KEY `role_permissions_roleId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `roles` DROP FOREIGN KEY `roles_tenantId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `user_roles` DROP FOREIGN KEY `user_roles_roleId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `user_roles` DROP FOREIGN KEY `user_roles_userId_fkey`;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE `users` DROP FOREIGN KEY `users_tenantId_fkey`;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE `tenants` DROP COLUMN `isActive`,
|
||||
ADD COLUMN `dbHost` VARCHAR(191) NOT NULL,
|
||||
ADD COLUMN `dbName` VARCHAR(191) NOT NULL,
|
||||
ADD COLUMN `dbPassword` VARCHAR(191) NOT NULL,
|
||||
ADD COLUMN `dbPort` INTEGER NOT NULL DEFAULT 3306,
|
||||
ADD COLUMN `dbUsername` VARCHAR(191) NOT NULL,
|
||||
ADD COLUMN `status` VARCHAR(191) NOT NULL DEFAULT 'active';
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `accounts`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `app_pages`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `apps`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `field_definitions`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `object_definitions`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `permissions`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `role_permissions`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `roles`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `user_roles`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `users`;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `domains` (
|
||||
`id` VARCHAR(191) NOT NULL,
|
||||
`domain` VARCHAR(191) NOT NULL,
|
||||
`tenantId` VARCHAR(191) NOT NULL,
|
||||
`isPrimary` BOOLEAN NOT NULL DEFAULT false,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `domains_domain_key`(`domain`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `domains` ADD CONSTRAINT `domains_tenantId_fkey` FOREIGN KEY (`tenantId`) REFERENCES `tenants`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -2,8 +2,10 @@ import { Module } from '@nestjs/common';
|
||||
import { AppBuilderService } from './app-builder.service';
|
||||
import { RuntimeAppController } from './runtime-app.controller';
|
||||
import { SetupAppController } from './setup-app.controller';
|
||||
import { TenantModule } from '../tenant/tenant.module';
|
||||
|
||||
@Module({
|
||||
imports: [TenantModule],
|
||||
providers: [AppBuilderService],
|
||||
controllers: [RuntimeAppController, SetupAppController],
|
||||
exports: [AppBuilderService],
|
||||
|
||||
@@ -1,44 +1,26 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { TenantDatabaseService } from '../tenant/tenant-database.service';
|
||||
import { App } from '../models/app.model';
|
||||
import { AppPage } from '../models/app-page.model';
|
||||
import { ObjectDefinition } from '../models/object-definition.model';
|
||||
|
||||
@Injectable()
|
||||
export class AppBuilderService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(private tenantDbService: TenantDatabaseService) {}
|
||||
|
||||
// Runtime endpoints
|
||||
async getApps(tenantId: string, userId: string) {
|
||||
// For now, return all active apps for the tenant
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
// For now, return all apps
|
||||
// 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' },
|
||||
});
|
||||
return App.query(knex).withGraphFetched('pages').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' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
const app = await App.query(knex)
|
||||
.findOne({ slug })
|
||||
.withGraphFetched('pages');
|
||||
|
||||
if (!app) {
|
||||
throw new NotFoundException(`App ${slug} not found`);
|
||||
@@ -53,23 +35,12 @@ export class AppBuilderService {
|
||||
pageSlug: string,
|
||||
userId: string,
|
||||
) {
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
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 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
const page = await AppPage.query(knex).findOne({
|
||||
appId: app.id,
|
||||
slug: pageSlug,
|
||||
});
|
||||
|
||||
if (!page) {
|
||||
@@ -81,31 +52,15 @@ export class AppBuilderService {
|
||||
|
||||
// Setup endpoints
|
||||
async getAllApps(tenantId: string) {
|
||||
return this.prisma.app.findMany({
|
||||
where: { tenantId },
|
||||
include: {
|
||||
pages: {
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
},
|
||||
},
|
||||
orderBy: { label: 'asc' },
|
||||
});
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
return App.query(knex).withGraphFetched('pages').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' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
const app = await App.query(knex)
|
||||
.findOne({ slug })
|
||||
.withGraphFetched('pages');
|
||||
|
||||
if (!app) {
|
||||
throw new NotFoundException(`App ${slug} not found`);
|
||||
@@ -120,14 +75,12 @@ export class AppBuilderService {
|
||||
slug: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
},
|
||||
) {
|
||||
return this.prisma.app.create({
|
||||
data: {
|
||||
tenantId,
|
||||
...data,
|
||||
},
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
return App.query(knex).insert({
|
||||
...data,
|
||||
displayOrder: 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -137,16 +90,12 @@ export class AppBuilderService {
|
||||
data: {
|
||||
label?: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
isActive?: boolean;
|
||||
},
|
||||
) {
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
const app = await this.getAppForSetup(tenantId, slug);
|
||||
|
||||
return this.prisma.app.update({
|
||||
where: { id: app.id },
|
||||
data,
|
||||
});
|
||||
return App.query(knex).patchAndFetchById(app.id, data);
|
||||
}
|
||||
|
||||
async createPage(
|
||||
@@ -157,37 +106,19 @@ export class AppBuilderService {
|
||||
label: string;
|
||||
type: string;
|
||||
objectApiName?: string;
|
||||
config?: any;
|
||||
sortOrder?: number;
|
||||
},
|
||||
) {
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
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,
|
||||
},
|
||||
return AppPage.query(knex).insert({
|
||||
appId: app.id,
|
||||
slug: data.slug,
|
||||
label: data.label,
|
||||
type: data.type,
|
||||
objectApiName: data.objectApiName,
|
||||
displayOrder: data.sortOrder || 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -199,44 +130,24 @@ export class AppBuilderService {
|
||||
label?: string;
|
||||
type?: string;
|
||||
objectApiName?: string;
|
||||
config?: any;
|
||||
sortOrder?: number;
|
||||
isActive?: boolean;
|
||||
},
|
||||
) {
|
||||
const knex = await this.tenantDbService.getTenantKnex(tenantId);
|
||||
const app = await this.getAppForSetup(tenantId, appSlug);
|
||||
|
||||
const page = await this.prisma.appPage.findFirst({
|
||||
where: {
|
||||
appId: app.id,
|
||||
slug: pageSlug,
|
||||
},
|
||||
const page = await AppPage.query(knex).findOne({
|
||||
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,
|
||||
},
|
||||
return AppPage.query(knex).patchAndFetchById(page.id, {
|
||||
...data,
|
||||
displayOrder: data.sortOrder,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { TenantId } from '../tenant/tenant.decorator';
|
||||
|
||||
@Controller('setup/apps')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
//@UseGuards(JwtAuthGuard)
|
||||
export class SetupAppController {
|
||||
constructor(private appBuilderService: AppBuilderService) {}
|
||||
|
||||
@@ -59,11 +59,6 @@ export class SetupAppController {
|
||||
@Param('pageSlug') pageSlug: string,
|
||||
@Body() data: any,
|
||||
) {
|
||||
return this.appBuilderService.updatePage(
|
||||
tenantId,
|
||||
appSlug,
|
||||
pageSlug,
|
||||
data,
|
||||
);
|
||||
return this.appBuilderService.updatePage(tenantId, appSlug, pageSlug, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BaseModel } from './base.model';
|
||||
import { App } from './app.model';
|
||||
|
||||
export class AppPage extends BaseModel {
|
||||
static tableName = 'app_pages';
|
||||
@@ -14,7 +15,7 @@ export class AppPage extends BaseModel {
|
||||
static relationMappings = {
|
||||
app: {
|
||||
relation: BaseModel.BelongsToOneRelation,
|
||||
modelClass: 'app.model',
|
||||
modelClass: App,
|
||||
join: {
|
||||
from: 'app_pages.appId',
|
||||
to: 'apps.id',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BaseModel } from './base.model';
|
||||
import { AppPage } from './app-page.model';
|
||||
|
||||
export class App extends BaseModel {
|
||||
static tableName = 'apps';
|
||||
@@ -12,7 +13,7 @@ export class App extends BaseModel {
|
||||
static relationMappings = {
|
||||
pages: {
|
||||
relation: BaseModel.HasManyRelation,
|
||||
modelClass: 'app-page.model',
|
||||
modelClass: AppPage,
|
||||
join: {
|
||||
from: 'apps.id',
|
||||
to: 'app_pages.appId',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Model, ModelOptions, QueryContext } from 'objection';
|
||||
import { Model, ModelOptions, QueryContext, snakeCaseMappers } from 'objection';
|
||||
|
||||
export class BaseModel extends Model {
|
||||
static columnNameMappers = snakeCaseMappers();
|
||||
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
|
||||
@@ -30,7 +30,7 @@ export class SchemaManagementService {
|
||||
|
||||
// Custom fields from field definitions
|
||||
for (const field of fields) {
|
||||
this.addFieldToTable(table, field);
|
||||
this.addFieldColumn(table, field);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ export class SchemaManagementService {
|
||||
const tableName = this.getTableName(objectApiName);
|
||||
|
||||
await knex.schema.alterTable(tableName, (table) => {
|
||||
this.addFieldToTable(table, field);
|
||||
this.addFieldColumn(table, field);
|
||||
});
|
||||
|
||||
this.logger.log(`Added field ${field.apiName} to table ${tableName}`);
|
||||
@@ -85,7 +85,7 @@ export class SchemaManagementService {
|
||||
/**
|
||||
* Add a field column to a table builder
|
||||
*/
|
||||
private addFieldToTable(
|
||||
private addFieldColumn(
|
||||
table: Knex.CreateTableBuilder | Knex.AlterTableBuilder,
|
||||
field: FieldDefinition,
|
||||
) {
|
||||
|
||||
@@ -180,8 +180,9 @@ export class TenantProvisioningService {
|
||||
|
||||
try {
|
||||
// Create default roles
|
||||
const [adminRoleId] = await tenantKnex('roles').insert({
|
||||
id: tenantKnex.raw('(UUID())'),
|
||||
const adminRoleId = crypto.randomUUID();
|
||||
await tenantKnex('roles').insert({
|
||||
id: adminRoleId,
|
||||
name: 'Admin',
|
||||
guardName: 'api',
|
||||
description: 'Full system administrator access',
|
||||
@@ -189,8 +190,9 @@ export class TenantProvisioningService {
|
||||
updated_at: tenantKnex.fn.now(),
|
||||
});
|
||||
|
||||
const [userRoleId] = await tenantKnex('roles').insert({
|
||||
id: tenantKnex.raw('(UUID())'),
|
||||
const userRoleId = crypto.randomUUID();
|
||||
await tenantKnex('roles').insert({
|
||||
id: userRoleId,
|
||||
name: 'User',
|
||||
guardName: 'api',
|
||||
description: 'Standard user access',
|
||||
@@ -212,7 +214,7 @@ export class TenantProvisioningService {
|
||||
|
||||
for (const perm of permissions) {
|
||||
await tenantKnex('permissions').insert({
|
||||
id: tenantKnex.raw('(UUID())'),
|
||||
id: crypto.randomUUID(),
|
||||
name: perm.name,
|
||||
guardName: 'api',
|
||||
description: perm.description,
|
||||
@@ -225,7 +227,7 @@ export class TenantProvisioningService {
|
||||
const allPermissions = await tenantKnex('permissions').select('id');
|
||||
for (const perm of allPermissions) {
|
||||
await tenantKnex('role_permissions').insert({
|
||||
id: tenantKnex.raw('(UUID())'),
|
||||
id: crypto.randomUUID(),
|
||||
roleId: adminRoleId,
|
||||
permissionId: perm.id,
|
||||
created_at: tenantKnex.fn.now(),
|
||||
@@ -239,7 +241,7 @@ export class TenantProvisioningService {
|
||||
.select('id');
|
||||
for (const perm of userPermissions) {
|
||||
await tenantKnex('role_permissions').insert({
|
||||
id: tenantKnex.raw('(UUID())'),
|
||||
id: crypto.randomUUID(),
|
||||
roleId: userRoleId,
|
||||
permissionId: perm.id,
|
||||
created_at: tenantKnex.fn.now(),
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
export const useApi = () => {
|
||||
const config = useRuntimeConfig()
|
||||
const apiBaseUrl = config.public.apiBaseUrl
|
||||
|
||||
// Use current domain for API calls (same subdomain routing)
|
||||
const getApiBaseUrl = () => {
|
||||
if (import.meta.client) {
|
||||
// In browser, use current hostname but with port 3000 for API
|
||||
const currentHost = window.location.hostname
|
||||
const protocol = window.location.protocol
|
||||
return `${protocol}//${currentHost}:3000`
|
||||
}
|
||||
// Fallback for SSR
|
||||
return config.public.apiBaseUrl
|
||||
}
|
||||
|
||||
const getHeaders = () => {
|
||||
const headers: Record<string, string> = {
|
||||
@@ -25,7 +36,7 @@ export const useApi = () => {
|
||||
|
||||
const api = {
|
||||
async get(path: string) {
|
||||
const response = await fetch(`${apiBaseUrl}/api${path}`, {
|
||||
const response = await fetch(`${getApiBaseUrl()}/api${path}`, {
|
||||
headers: getHeaders(),
|
||||
})
|
||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
|
||||
@@ -33,7 +44,7 @@ export const useApi = () => {
|
||||
},
|
||||
|
||||
async post(path: string, data: any) {
|
||||
const response = await fetch(`${apiBaseUrl}/api${path}`, {
|
||||
const response = await fetch(`${getApiBaseUrl()}/api${path}`, {
|
||||
method: 'POST',
|
||||
headers: getHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
@@ -43,7 +54,7 @@ export const useApi = () => {
|
||||
},
|
||||
|
||||
async put(path: string, data: any) {
|
||||
const response = await fetch(`${apiBaseUrl}/api${path}`, {
|
||||
const response = await fetch(`${getApiBaseUrl()}/api${path}`, {
|
||||
method: 'PUT',
|
||||
headers: getHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
@@ -53,7 +64,7 @@ export const useApi = () => {
|
||||
},
|
||||
|
||||
async delete(path: string) {
|
||||
const response = await fetch(`${apiBaseUrl}/api${path}`, {
|
||||
const response = await fetch(`${getApiBaseUrl()}/api${path}`, {
|
||||
method: 'DELETE',
|
||||
headers: getHeaders(),
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ services:
|
||||
context: ../backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: platform-api
|
||||
command: npm run start:dev
|
||||
command: npm run start:dev -- --host 0.0.0.0
|
||||
env_file:
|
||||
- ../.env.api
|
||||
ports:
|
||||
|
||||
Reference in New Issue
Block a user