mirror of
https://github.com/kottster/kottster.git
synced 2026-01-09 06:12:22 +08:00
Production build update (#87)
* docs: Replace Crisp with Discord * feat: Optimize production build * feat: Add Dockerfile and docker-compose.yml templates
This commit is contained in:
parent
ab7fd69e8c
commit
162df86c4d
@ -60,7 +60,7 @@ representative at an online or offline event.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[team@kottster.io](team@kottster.io).
|
||||
[team@kottster.app](team@kottster.app).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
||||
@ -91,7 +91,7 @@ See [CONTRIBUTING.md](https://github.com/kottster/kottster/blob/main/CONTRIBUTIN
|
||||
|
||||
- 💬 [Join our Discord](https://discord.com/invite/Qce9uUqK98)
|
||||
- 📬 [Contact us](https://kottster.app/contact-us)
|
||||
- ✉️ [team@kottster.io](mailto:team@kottster.io)
|
||||
- ✉️ [team@kottster.app](mailto:team@kottster.app)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ export default defineConfig({
|
||||
'script',
|
||||
{
|
||||
src: '/docs/js/discord.js',
|
||||
defer: 'true',
|
||||
defer: 'true'
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -107,8 +107,6 @@ export default defineConfig({
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
|
||||
sitemap: {
|
||||
hostname: 'https://kottster.app',
|
||||
},
|
||||
|
||||
@ -4,13 +4,35 @@ import fs from 'fs/promises';
|
||||
import { checkTsUsage } from '@kottster/common';
|
||||
|
||||
export async function buildServer(): Promise<void> {
|
||||
const projectDir = process.cwd();
|
||||
const usingTsc = checkTsUsage();
|
||||
|
||||
// Find all api.server.js files in the app/pages directory
|
||||
const filenameEnding = `.server.${usingTsc ? 'ts' : 'js'}`;
|
||||
const projectDir = process.cwd();
|
||||
const pagesDir = path.join(projectDir, 'app/pages');
|
||||
const apiFiles: string[] = [];
|
||||
const dataSourcesDir = path.join(projectDir, 'app/_server/data-sources');
|
||||
|
||||
// Find all app/pages/<pageKey>/page.json
|
||||
const pageJsonFiles: string[] = [];
|
||||
try {
|
||||
const dirs = await fs.readdir(pagesDir, { withFileTypes: true });
|
||||
|
||||
for (const dir of dirs) {
|
||||
if (dir.isDirectory()) {
|
||||
const dirPath = path.join(pagesDir, dir.name);
|
||||
const files = await fs.readdir(dirPath);
|
||||
|
||||
for (const file of files) {
|
||||
if (file === 'page.json') {
|
||||
pageJsonFiles.push(path.join(dirPath, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Could not read pages directory:', error);
|
||||
}
|
||||
|
||||
// Find all app/pages/<pageKey>/api.server.js or .ts files
|
||||
const filenameEnding = `.server.${usingTsc ? 'ts' : 'js'}`;
|
||||
const pageApiFiles: string[] = [];
|
||||
try {
|
||||
const dirs = await fs.readdir(pagesDir, { withFileTypes: true });
|
||||
|
||||
@ -21,7 +43,7 @@ export async function buildServer(): Promise<void> {
|
||||
|
||||
for (const file of files) {
|
||||
if (file.endsWith(filenameEnding)) {
|
||||
apiFiles.push(path.join(dirPath, file));
|
||||
pageApiFiles.push(path.join(dirPath, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,8 +52,26 @@ export async function buildServer(): Promise<void> {
|
||||
console.warn('Could not read pages directory:', error);
|
||||
}
|
||||
|
||||
// Find all data source files in the app/_server/data-sources directory
|
||||
const dataSourcesDir = path.join(projectDir, 'app/_server/data-sources');
|
||||
// Find all app/_server/data-sources/<dataSource>/dataSource.json
|
||||
const dataSourceJsonFiles: string[] = [];
|
||||
try {
|
||||
const dirs = await fs.readdir(dataSourcesDir, { withFileTypes: true });
|
||||
for (const dir of dirs) {
|
||||
if (dir.isDirectory()) {
|
||||
const dirPath = path.join(dataSourcesDir, dir.name);
|
||||
const files = await fs.readdir(dirPath);
|
||||
for (const file of files) {
|
||||
if (file === 'dataSource.json') {
|
||||
dataSourceJsonFiles.push(path.join(dirPath, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Could not read data sources directory:', error);
|
||||
}
|
||||
|
||||
// Find all app/_server/data-sources/<dataSource>/index.js or .ts files
|
||||
const dataSourceFiles: string[] = [];
|
||||
try {
|
||||
const dirs = await fs.readdir(dataSourcesDir, { withFileTypes: true });
|
||||
@ -53,7 +93,7 @@ export async function buildServer(): Promise<void> {
|
||||
const input = {
|
||||
server: `app/_server/server.${usingTsc ? 'ts' : 'js'}`,
|
||||
...Object.fromEntries(
|
||||
apiFiles.map(file => [
|
||||
pageApiFiles.map(file => [
|
||||
file.replace(path.join(projectDir, 'app/'), '').replace(filenameEnding, ''),
|
||||
file
|
||||
])
|
||||
@ -63,7 +103,7 @@ export async function buildServer(): Promise<void> {
|
||||
file.replace(path.join(projectDir, 'app/_server/'), '').replace(`.${usingTsc ? 'ts' : 'js'}`, ''),
|
||||
file
|
||||
])
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
try {
|
||||
@ -86,7 +126,30 @@ export async function buildServer(): Promise<void> {
|
||||
'@': '/app'
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Copy app/pages/<pageKey>/page.json files
|
||||
for (const file of pageJsonFiles) {
|
||||
const destPath = path.join(
|
||||
projectDir,
|
||||
'dist/server',
|
||||
path.relative(path.join(projectDir, 'app'), file)
|
||||
);
|
||||
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
||||
await fs.copyFile(file, destPath);
|
||||
}
|
||||
|
||||
// Copy app/_server/data-sources/<dataSource>/dataSource.json files
|
||||
for (const file of dataSourceJsonFiles) {
|
||||
const destPath = path.join(
|
||||
projectDir,
|
||||
'dist/server',
|
||||
path.relative(path.join(projectDir, 'app/_server'), file)
|
||||
);
|
||||
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
||||
await fs.copyFile(file, destPath);
|
||||
}
|
||||
|
||||
console.log('Build completed successfully!');
|
||||
} catch (error) {
|
||||
console.error('Build failed:', error)
|
||||
|
||||
@ -45,7 +45,12 @@ export async function startProjectDev(options: Options): Promise<void> {
|
||||
const serverEnv = {
|
||||
...process.env,
|
||||
|
||||
// Set NODE_ENV=development just in case
|
||||
NODE_ENV: 'development',
|
||||
|
||||
// Kottster app uses it's environment variable to determine the stage,
|
||||
KOTTSTER_APP_STAGE: 'development',
|
||||
VITE_KOTTSTER_APP_STAGE: 'development',
|
||||
|
||||
DEV_API_SERVER_PORT: devApiServerPortStr,
|
||||
VITE_DEV_API_SERVER_PORT: devApiServerPortStr,
|
||||
|
||||
@ -75,6 +75,8 @@ export class FileCreator {
|
||||
|
||||
// Create files
|
||||
this.createFileFromTemplate('vite.config.js', path.join(this.projectDir, `vite.config.${this.jsExt}`));
|
||||
this.createFileFromTemplate('Dockerfile', path.join(this.projectDir, 'Dockerfile'));
|
||||
this.createFileFromTemplate('docker-compose.yml', path.join(this.projectDir, 'docker-compose.yml'));
|
||||
this.createFileFromTemplate('app/index.html', path.join(this.projectDir, `app/index.html`));
|
||||
this.createFileFromTemplate('app/main.jsx', path.join(this.projectDir, `app/main.${this.jsxExt}`));
|
||||
this.createFileFromTemplate('app/_server/app.js', path.join(this.projectDir, `app/_server/app.${this.jsExt}`));
|
||||
|
||||
@ -3,6 +3,8 @@ import { stripIndent } from "@kottster/common";
|
||||
type TemplateVars = {
|
||||
'vite.config.js': undefined;
|
||||
'tsconfig.json': undefined;
|
||||
'Dockerfile': undefined;
|
||||
'docker-compose.yml': undefined;
|
||||
'app/_server/app.js': undefined;
|
||||
'app/_server/server.js': undefined;
|
||||
'app/_server/data-sources/postgres/index.js': {
|
||||
@ -127,6 +129,48 @@ export class FileTemplateManager {
|
||||
}
|
||||
`),
|
||||
|
||||
'Dockerfile': stripIndent(`
|
||||
# For production deployment
|
||||
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY --from=builder /app/dist ./dist
|
||||
|
||||
ENV PORT=3000
|
||||
EXPOSE $PORT
|
||||
|
||||
CMD ["node", "dist/server/server.cjs"]
|
||||
`),
|
||||
|
||||
'docker-compose.yml': stripIndent(`
|
||||
# For production deployment
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- PORT=3000
|
||||
`),
|
||||
|
||||
'app/_server/app.js': stripIndent(`
|
||||
import { createApp } from '@kottster/server';
|
||||
import schema from '../../kottster-app.json';
|
||||
|
||||
8
packages/cli/package-lock.json
generated
8
packages/cli/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kottster/cli",
|
||||
"version": "3.0.2",
|
||||
"version": "3.1.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -1110,9 +1110,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@kottster/common": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.yarnpkg.com/@kottster/common/-/common-3.0.1.tgz",
|
||||
"integrity": "sha1-NcFT/q4T03aRu5st/jpk5L35rFA= sha512-wOUDHFfwh/gMtOGHiZeFdR4nG3NawzFbLCIdX1UuYOxgYstr1E4kUNFJL6yXKGpLcx06lxwv4o4x0DZdh3Ldmg==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.yarnpkg.com/@kottster/common/-/common-3.0.6.tgz",
|
||||
"integrity": "sha1-Y1cetuDb3maVnD0D7ARxe1AS0co= sha512-38ubX4CmJXTRDFXAodSyEoJgIzJk2PE2Y1nADldxk/tgglzYn7BD0xE6Xj/7Ly4X38glUQNObb59KhDYdfvVDQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kottster/cli",
|
||||
"version": "3.0.2",
|
||||
"version": "3.1.1",
|
||||
"description": "CLI for Kottster",
|
||||
"main": "dist/index.js",
|
||||
"license": "Apache-2.0",
|
||||
@ -32,7 +32,7 @@
|
||||
"@babel/traverse": "^7.24.1",
|
||||
"@babel/types": "^7.24.0",
|
||||
"@eslint/js": "^9.2.0",
|
||||
"@kottster/common": "^3.0.1",
|
||||
"@kottster/common": "^3.0.6",
|
||||
"@types/babel__traverse": "^7.20.5",
|
||||
"@types/cross-spawn": "^6.0.6",
|
||||
"@types/dotenv": "^8.2.0",
|
||||
|
||||
@ -899,10 +899,10 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@kottster/common@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@kottster/common/-/common-3.0.1.tgz#35c153feae13d37691bb9b2dfe3a64e4bdf9ac50"
|
||||
integrity sha512-wOUDHFfwh/gMtOGHiZeFdR4nG3NawzFbLCIdX1UuYOxgYstr1E4kUNFJL6yXKGpLcx06lxwv4o4x0DZdh3Ldmg==
|
||||
"@kottster/common@^3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@kottster/common/-/common-3.0.6.tgz#63571eb6e0dbde66959c3d03ec04717b5012d1ca"
|
||||
integrity sha512-38ubX4CmJXTRDFXAodSyEoJgIzJk2PE2Y1nADldxk/tgglzYn7BD0xE6Xj/7Ly4X38glUQNObb59KhDYdfvVDQ==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PageFileStructure } from "@kottster/common";
|
||||
import { PageFileStructure, Stage } from "@kottster/common";
|
||||
import { DevAction } from "../models/action.model";
|
||||
import { FileReader } from "../services/fileReader.service";
|
||||
import { FileWriter } from "../services/fileWriter.service";
|
||||
@ -14,7 +14,7 @@ interface Data {
|
||||
export class CreatePage extends DevAction {
|
||||
public async executeDevAction(data: Data) {
|
||||
const fileWriter = new FileWriter({ usingTsc: this.app.usingTsc });
|
||||
const fileReader = new FileReader();
|
||||
const fileReader = new FileReader(this.app.stage === Stage.development);
|
||||
const appSchema = fileReader.readSchemaJsonFile();
|
||||
|
||||
// Add page file
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Stage } from "@kottster/common";
|
||||
import { DevAction } from "../models/action.model";
|
||||
import { FileReader } from "../services/fileReader.service";
|
||||
import { FileWriter } from "../services/fileWriter.service";
|
||||
@ -12,7 +13,7 @@ interface Data {
|
||||
export class DeletePage extends DevAction {
|
||||
public async executeDevAction(data: Data) {
|
||||
const fileWriter = new FileWriter({ usingTsc: this.app.usingTsc });
|
||||
const fileReader = new FileReader();
|
||||
const fileReader = new FileReader(this.app.stage === Stage.development);
|
||||
const { key } = data;
|
||||
const appSchema = fileReader.readSchemaJsonFile();
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ClientAppSchema } from "@kottster/common";
|
||||
import { ClientAppSchema, Page, Stage } from "@kottster/common";
|
||||
import { Action } from "../models/action.model";
|
||||
import { FileReader } from "../services/fileReader.service";
|
||||
|
||||
@ -6,10 +6,22 @@ import { FileReader } from "../services/fileReader.service";
|
||||
* Get the app schema
|
||||
*/
|
||||
export class GetAppSchema extends Action {
|
||||
private cachedPages: Page[] | null = null;
|
||||
|
||||
public async execute(): Promise<ClientAppSchema> {
|
||||
const fileReader = new FileReader();
|
||||
const appSchema = fileReader.readSchemaJsonFile();
|
||||
const pages = fileReader.getPageConfigs();
|
||||
const fileReader = new FileReader(this.app.stage === Stage.development);
|
||||
|
||||
// Cache pages in production to avoid reading files every time
|
||||
const pages = this.app.stage === Stage.production && this.cachedPages
|
||||
? this.cachedPages
|
||||
: fileReader.getPageConfigs();
|
||||
|
||||
if (this.app.stage === Stage.production && !this.cachedPages) {
|
||||
this.cachedPages = pages;
|
||||
}
|
||||
|
||||
// In production, use the in-memory schema; in development, read from file
|
||||
const appSchema = this.app.stage === Stage.production ? this.app.schema : fileReader.readSchemaJsonFile();
|
||||
|
||||
return {
|
||||
...appSchema,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { AppSchema } from "@kottster/common";
|
||||
import { AppSchema, Stage } from "@kottster/common";
|
||||
import { DevAction } from "../models/action.model";
|
||||
import { FileReader } from "../services/fileReader.service";
|
||||
import { FileWriter } from "../services/fileWriter.service";
|
||||
@ -13,7 +13,7 @@ interface Data {
|
||||
export class UpdateAppSchema extends DevAction {
|
||||
public async executeDevAction(data: Data) {
|
||||
const fileWriter = new FileWriter({ usingTsc: this.app.usingTsc });
|
||||
const fileReader = new FileReader();
|
||||
const fileReader = new FileReader(this.app.stage === Stage.development);
|
||||
const { menuPageOrder } = data;
|
||||
const appSchema = fileReader.readSchemaJsonFile();
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Page } from "@kottster/common";
|
||||
import { Page, Stage } from "@kottster/common";
|
||||
import { DevAction } from "../models/action.model";
|
||||
import { FileReader } from "../services/fileReader.service";
|
||||
import { FileWriter } from "../services/fileWriter.service";
|
||||
@ -14,7 +14,7 @@ interface Data {
|
||||
export class UpdatePage extends DevAction {
|
||||
public async executeDevAction(data: Data) {
|
||||
const fileWriter = new FileWriter({ usingTsc: this.app.usingTsc });
|
||||
const fileReader = new FileReader();
|
||||
const fileReader = new FileReader(this.app.stage === Stage.development);
|
||||
const { key, page } = data;
|
||||
const appSchema = fileReader.readSchemaJsonFile();
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ export class KottsterApp {
|
||||
private readonly secretKey: string;
|
||||
public readonly usingTsc: boolean;
|
||||
public readonly readOnlyMode: boolean = false;
|
||||
public readonly stage: Stage = process.env.NODE_ENV === Stage.development ? Stage.development : Stage.production;
|
||||
public readonly stage: Stage = process.env.KOTTSTER_APP_STAGE === Stage.development ? Stage.development : Stage.production;
|
||||
public dataSources: DataSource[] = [];
|
||||
public schema: AppSchema;
|
||||
private customEnsureValidToken?: (request: Request) => Promise<EnsureValidTokenResponse>;
|
||||
@ -123,7 +123,7 @@ export class KottsterApp {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error handling internal API request:', error);
|
||||
console.error('Internal API error:', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
return;
|
||||
}
|
||||
@ -153,7 +153,7 @@ export class KottsterApp {
|
||||
result: await this.executeAction(action, actionData),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error handling Kottster API request:', error);
|
||||
console.error('Kottster API error:', error);
|
||||
|
||||
result = {
|
||||
status: 'error',
|
||||
@ -472,7 +472,7 @@ export class KottsterApp {
|
||||
let user: User;
|
||||
|
||||
if (this.schema.enterpriseHub) {
|
||||
const response = await fetch(`${this.schema.enterpriseHub.url}/apps/${this.appId}/users/current`, {
|
||||
const response = await fetch(`${this.schema.enterpriseHub.url}/v1/apps/${this.appId}/users/current`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
|
||||
@ -57,8 +57,7 @@ export class KottsterServer {
|
||||
}
|
||||
|
||||
private setupServiceRoutes() {
|
||||
this.expressApp.use('/internal-api/', this.app.getInternalApiRoute());
|
||||
// this.expressApp.use('/devsync-api/', this.app.getDevSyncApiRoute());
|
||||
this.expressApp.use('/internal-api', this.app.getInternalApiRoute());
|
||||
}
|
||||
|
||||
private setupWebSocketHealthCheck() {
|
||||
@ -87,7 +86,7 @@ export class KottsterServer {
|
||||
|
||||
private async setupDynamicDataSources() {
|
||||
const isDevelopment = this.app.stage === Stage.development;
|
||||
const fileReader = new FileReader();
|
||||
const fileReader = new FileReader(isDevelopment);
|
||||
|
||||
// Dynamically load data sources from the data-sources directory
|
||||
const dataSourcesDir = isDevelopment ? `${PROJECT_DIR}/app/_server/data-sources` : `${PROJECT_DIR}/dist/server/data-sources`;
|
||||
@ -96,7 +95,6 @@ export class KottsterServer {
|
||||
if (!fs.existsSync(dataSourcesDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSources: DataSource[] = [];
|
||||
const dataSourceConfigs = fileReader.getDataSourceConfigs();
|
||||
|
||||
@ -138,40 +136,43 @@ export class KottsterServer {
|
||||
|
||||
private async setupDynamicRoutes() {
|
||||
const isDevelopment = this.app.stage === Stage.development;
|
||||
const fileReader = new FileReader();
|
||||
const fileReader = new FileReader(isDevelopment);
|
||||
const pageConfigs = fileReader.getPageConfigs();
|
||||
|
||||
// Set routes for pages specified in the schema
|
||||
if (pageConfigs) {
|
||||
for (const pageConfig of pageConfigs) {
|
||||
const pagesDir = isDevelopment ? `${PROJECT_DIR}/app/pages` : `${PROJECT_DIR}/dist/server/pages`;
|
||||
const usingTsc = this.app.usingTsc;
|
||||
const apiPath = path.join(pagesDir, pageConfig.key, isDevelopment ? `api.server.${usingTsc ? 'ts' : 'js'}` : 'api.cjs');
|
||||
|
||||
// If the page is custom or has a defined api.server.js file, load it
|
||||
if (pageConfig.type === 'custom' || fs.existsSync(apiPath)) {
|
||||
try {
|
||||
const routeModule = await import(apiPath);
|
||||
if (routeModule.default && typeof routeModule.default === 'function') {
|
||||
const routePath = `/api/${pageConfig.key}`;
|
||||
|
||||
this.expressApp.post(routePath, this.app.createRequestWithPageDataMiddleware(pageConfig), routeModule.default);
|
||||
try {
|
||||
const pagesDir = isDevelopment ? `${PROJECT_DIR}/app/pages` : `${PROJECT_DIR}/dist/server/pages`;
|
||||
const usingTsc = this.app.usingTsc;
|
||||
const apiPath = path.join(pagesDir, pageConfig.key, isDevelopment ? `api.server.${usingTsc ? 'ts' : 'js'}` : 'api.cjs');
|
||||
|
||||
// If the page is custom or has a defined api.server.js file, load it
|
||||
if (pageConfig.type === 'custom' || fs.existsSync(apiPath)) {
|
||||
try {
|
||||
const routeModule = await import(apiPath);
|
||||
if (routeModule.default && typeof routeModule.default === 'function') {
|
||||
const routePath = `/api/${pageConfig.key}`;
|
||||
|
||||
this.expressApp.post(routePath, this.app.createRequestWithPageDataMiddleware(pageConfig), routeModule.default);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to load route "${pageConfig.key}":`, error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to load route "${pageConfig.key}":`, error);
|
||||
}
|
||||
} else {
|
||||
if (pageConfig.type === 'table') {
|
||||
if (!pageConfig.config.dataSource) {
|
||||
console.warn(`Page "${pageConfig.key}" does not have a data source specified. Skipping route setup.`);
|
||||
continue;
|
||||
} else {
|
||||
if (pageConfig.type === 'table') {
|
||||
if (!pageConfig.config.dataSource) {
|
||||
console.warn(`Page "${pageConfig.key}" does not have a data source specified. Skipping route setup.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.expressApp.post(`/api/${pageConfig.key}`, this.app.createRequestWithPageDataMiddleware(pageConfig), this.app.defineTableController(pageConfig.config));
|
||||
}
|
||||
if (pageConfig.type === 'dashboard') {
|
||||
this.expressApp.post(`/api/${pageConfig.key}`, this.app.createRequestWithPageDataMiddleware(pageConfig), this.app.defineDashboardController(pageConfig.config));
|
||||
}
|
||||
|
||||
this.expressApp.post(`/api/${pageConfig.key}`, this.app.createRequestWithPageDataMiddleware(pageConfig), this.app.defineTableController(pageConfig.config));
|
||||
}
|
||||
if (pageConfig.type === 'dashboard') {
|
||||
this.expressApp.post(`/api/${pageConfig.key}`, this.app.createRequestWithPageDataMiddleware(pageConfig), this.app.defineDashboardController(pageConfig.config));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error setting up route for page "${pageConfig.key}":`, error);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -196,7 +197,7 @@ export class KottsterServer {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Client directory not found: ${clientDir}`);
|
||||
console.warn(`Client directory not found: ${clientDir}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import { PageFileStructure, File, AppSchema, Page, DataSource } from "@kottster/
|
||||
* Service for reading files
|
||||
*/
|
||||
export class FileReader {
|
||||
constructor(private readonly isDevelopment?: boolean) {}
|
||||
|
||||
/**
|
||||
* Read the schema from the kottster-app.json file
|
||||
@ -40,7 +41,7 @@ export class FileReader {
|
||||
* @returns The page directories
|
||||
*/
|
||||
public getPagesDirectories(): string[] {
|
||||
const dir = `${PROJECT_DIR}/app/pages`;
|
||||
const dir = this.isDevelopment ? `${PROJECT_DIR}/app/pages` : `${PROJECT_DIR}/dist/server/pages`;
|
||||
if (!fs.existsSync(dir)) {
|
||||
return [];
|
||||
}
|
||||
@ -53,7 +54,7 @@ export class FileReader {
|
||||
* @returns The data source directories
|
||||
*/
|
||||
public getDataSourceDirectories(): string[] {
|
||||
const dir = `${PROJECT_DIR}/app/_server/data-sources`;
|
||||
const dir = this.isDevelopment ? `${PROJECT_DIR}/app/_server/data-sources` : `${PROJECT_DIR}/dist/server/data-sources`;
|
||||
if (!fs.existsSync(dir)) {
|
||||
return [];
|
||||
}
|
||||
@ -70,7 +71,7 @@ export class FileReader {
|
||||
const result: Omit<DataSource, 'status' | 'adapter'>[] = [];
|
||||
|
||||
for (const dir of dataSourceDirectories) {
|
||||
const dataSourceJsonPath = path.join(PROJECT_DIR, `app/_server/data-sources/${dir}/dataSource.json`);
|
||||
const dataSourceJsonPath = path.join(PROJECT_DIR, this.isDevelopment ? `app/_server/data-sources/${dir}/dataSource.json` : `dist/server/data-sources/${dir}/dataSource.json`);
|
||||
if (!fs.existsSync(dataSourceJsonPath)) {
|
||||
console.warn(`Data source config not found for directory: ${dir}`);
|
||||
continue;
|
||||
@ -136,7 +137,7 @@ export class FileReader {
|
||||
* @returns The page structure or null if the page does not exist
|
||||
*/
|
||||
public getPageFileStructure(pageKey: string): PageFileStructure | null {
|
||||
const dirPath = `app/pages/${pageKey}`;
|
||||
const dirPath = this.isDevelopment ? `app/pages/${pageKey}` : `dist/server/pages/${pageKey}`;
|
||||
const absoluteDirPath = `${PROJECT_DIR}/${dirPath}`;
|
||||
|
||||
const filePaths = this.getAllFilePathsInDirectory(absoluteDirPath);
|
||||
|
||||
2
packages/server/package-lock.json
generated
2
packages/server/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kottster/server",
|
||||
"version": "3.0.7",
|
||||
"version": "3.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kottster/server",
|
||||
"version": "3.0.7",
|
||||
"version": "3.1.0",
|
||||
"description": "Instant admin panel for your project",
|
||||
"keywords": [
|
||||
"admin",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user