پرش به مطلب اصلی

ایجاد اپلیکیشن جدید

این راهنما نحوه ایجاد یک اپلیکیشن جدید در فریمورک کیمیا را بدون استفاده از ابزار CLI توضیح می‌دهد. این روش زمانی مفید است که ترجیح می‌دهید کنترل دستی بر فرآیند داشته باشید یا نیاز به سفارشی‌سازی ساختار دارید.

پیش‌نیازها

قبل از شروع، اطمینان حاصل کنید که موارد زیر را دارید:

  • Node.js نسخه ۱۴ یا بالاتر
  • یک پروژه فریمورک کیمیا راه‌اندازی شده

مراحل ایجاد اپلیکیشن

۱. انتخاب نام اپلیکیشن و زبان محلی

ابتدا تصمیم بگیرید درباره:

  • نام اپلیکیشن: یک شناسه منحصر به فرد (مثلاً my_app, account)
  • زبان محلی پیش‌فرض: en برای انگلیسی یا fa برای فارسی انتخاب کنید

۲. ایجاد ساختار دایرکتوری

دایرکتوری اصلی اپلیکیشن و زیرشاخه‌ها را ایجاد کنید:

mkdir my_app
cd my_app
mkdir -p app/{middlewares,models,views,events,apis}
mkdir -p resources/{assets,templates/layout}
mkdir -p resources/locales/en # or fa
mkdir -p routes

۳. ایجاد فایل‌های اصلی

app.ts (متادیتای اپلیکیشن)

فایل app.ts را در دایرکتوری ریشه ایجاد کنید:

import { AppMetaData } from "@core/interfaces";
import { config } from "@core/helpers";

export const appMetaData: AppMetaData = {
name: "my_app",
version: '0.1',
buildNumber: 1,
supportedLanguages: ['en'],
logo: {
favicon: 'favicon.png',
small: 'logo-small.png',
medium: 'logo-small.png',
large: 'logo-small.png',
},
title: {
fa: 'MyApp',
en: 'MyApp',
},
swagger: {
path: "/api-docs",
enabled: () => config<boolean>("DEBUG_MODE") === true,
},
};

میان‌افزارها (app/middlewares/middlewares.ts)

import { MiddlewareDefinition } from "@core/server";

export type AppMiddleware = '';
export const AppMiddlewares: MiddlewareDefinition<AppMiddleware>[] = [];

رویدادها (app/events/init.ts)

import { ApplicationEvent } from "@core/server";
import { AppEventType } from "@core/interfaces";

export class init extends ApplicationEvent {
static override type(): AppEventType {
return "init";
}

override async start() {
console.log('init my_app app!');
return true;
}
}

مدل‌ها (app/models/models.ts و interfaces.ts)

// interfaces.ts
// رابط‌های مدل خود را اینجا اضافه کنید

// models.ts
import { ModelDefinition } from "@core/server";

export type AppModelName = '';
export const AppModels: ModelDefinition<AppModelName>[] = [];

نماها (app/views/base.ts و views.ts)

// base.ts
import { ApplicationView } from "@core/server";

export class base extends ApplicationView {}

// views.ts
import { ViewDefinition } from "@core/server";

export type AppView = '';
export const AppViews: ViewDefinition<AppView>[] = [];

APIها (app/apis/base.ts و apis.ts)

// base.ts
import { ApplicationAPI } from "@core/server";
import { AppModelName } from "../models/models";
import { MyAppSystemConfigKey, MyAppSystemUserConfigKey } from "../interfaces";
import {
absUrl,
getConfig,
getUserConfig,
setConfig,
setUserConfig,
} from "@core/helpers";
import { ConfigDataType } from "@core/database";
import { appMetaData } from "../../app";

export class base extends ApplicationAPI {
get appName() {
return appMetaData.name;
}

async syncAppModel(modelName: AppModelName, argvs = []) {
return await this.syncModel<AppModelName>(modelName, argvs, this.appName);
}

appInfoLog(name: string, text?: any) {
this.infoV2Log(this.appName, name, text);
}

appErrorLog(name: string, text?: any) {
this.errorV2Log(this.appName, name, text);
}

async setMyAppConfig(
key: MyAppSystemConfigKey,
value: any,
dataType: ConfigDataType = "auto"
) {
return setConfig<MyAppSystemConfigKey, string>(
key,
this.appName,
value,
this.request.user().id,
dataType
);
}

async setMyAppUserConfig<T = any>(
key: MyAppSystemUserConfigKey,
value: T,
userId?: number
) {
if (!userId) {
userId = this.request.user()?.id;
}
if (!userId) return false;
return setUserConfig(key, userId, value, this.appName);
}

async getMyAppConfig<T = string>(key: MyAppSystemConfigKey, def?: T) {
return getConfig<T, MyAppSystemConfigKey, string>(
key,
this.appName,
def,
this.request.user()?.id
);
}

async getMyAppUserConfig<T = string>(
key: MyAppSystemUserConfigKey,
def?: T,
userId?: number
) {
if (!userId) {
userId = this.request.user()?.id;
}
if (!userId) return undefined;
return getUserConfig<T>(userId, key, this.appName, def);
}
}

// apis.ts
import { APIDefinition } from "@core/server";

export type AppAPI = '';
export const AppAPIs: APIDefinition<AppAPI>[] = [];

رابط‌ها (app/interfaces.ts)

export type MyAppSystemUserConfigKey ='';
export type MyAppSystemConfigKey='';

مدیریت خطا (app/error-handler.ts)

import { HttpErrorHandler } from "@core/server";
import { HttpStatusCode } from "@core/server";

export class AppErrorHandler extends HttpErrorHandler {
async webHandler(code: HttpStatusCode, data?: object) {
return await super.webHandler(code, data);
}
}

۴. ایجاد مسیرها

مسیرهای وب (routes/web.ts)

import { HttpRoute } from "@core/interfaces";
import { AppMiddleware } from '../app/middlewares/middlewares';
import { AppView } from "../app/views/views";

export const ROUTES: HttpRoute<AppView, AppMiddleware>[] = [
{
path: '/',
template: 'layout/base',
},
];

مسیرهای API (routes/api.ts)

import { HttpRoute } from "@core/interfaces";
import { AppMiddleware } from '../app/middlewares/middlewares';
import { AppAPI } from "../app/apis/apis";

export const ROUTES: HttpRoute<AppAPI, AppMiddleware>[] = [];

۵. ایجاد فایل‌های پیکربندی

package.json

{
"name": "my_app",
"version": "1.0.0",
"description": "",
"main": "build/local.js",
"scripts": {
"build": "tsc -b tsconfig.json",
"start": "node local.js",
"start:dev": "node build/local.js dev",
"dev": "concurrently \"tsc -b -w tsconfig.json\" \"nodemon --ext 'js' --ignore 'storage/*' --ignore 'resources/frontend' --ignore 'resources/assets' --trace-warnings build/local.js dev\""
},
"keywords": [],
"dependencies": {
"@kimia-framework/core": "^0.10.87",
"@core": "workspace:./node_modules/@kimia-framework/core",
"adm-zip": "^0.5.9",
"connect-multiparty": "^2.2.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"jsonwebtoken": "^9.0.2",
"live-plugin-manager": "^1.0.0",
"mime-types": "^2.1.35",
"multer": "^1.4.5-lts.1",
"node-machine-id": "^1.1.12",
"sequelize": "^6.37.5",
"signal-exit": "^4.1.0",
"sqlite3": "^5.0.2",
"swagger-ui-express": "^4.6.0",
"tslib": "^2.5.0",
"twing": "^5.0.2",
"typescript": "^4.9.5"
},
"devDependencies": {
"@types/adm-zip": "^0.5.0",
"@types/express": "^4.17.11",
"@types/multer": "^1.4.7",
"@types/node": "^14.18.42",
"@types/sequelize": "^4.28.9",
"concurrently": "^5.3.0",
"nodemon": "^3.1.4",
"source-map-support": "^0.5.21",
"typescript-json-schema": "^0.65.1"
},
"author": "your_name",
"license": "ISC"
}

tsconfig.json

{
"compilerOptions": {
"target": "es6",
"module": "CommonJS",
"resolveJsonModule": true,
"declaration": true,
"sourceMap": false,
"outDir": "./build",
"removeComments": false,
"importHelpers": false,
"strict": false,
"noImplicitReturns": true,
"moduleResolution": "node",
"types": ["node"],
"esModuleInterop": true,
"experimentalDecorators": true
},
"compileOnSave": true,
"exclude": [
"node_modules",
".vscode",
"build",
"storage",
"./**/frontend",
"./**/assets",
"build_minify"
]
}

local.ts

import { startMainApplication } from "@core";
import { Sequelize } from "sequelize";

startMainApplication("../settings.json", {
disableAutoInitApps: true,
Sequelize,
requireFn: require
});

۶. ایجاد منابع

قالب (resources/templates/layout/base.twing.html)

<!DOCTYPE html>
<html lang="{{ __('info.code')}}">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="{{asset('/favicon.png')}}" />
<title>
{{__('app_name')}}
</title>

<!-- font defined -->
<style>
@font-face {
font-family: "mainFont";
font-style: normal;
src: url("{{shared('fonts/' + __('info.defaultFontName'), true)}}") format("truetype");
}

:root {
--font-family-sans-serif: mainFont, -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}

html,
body {
font-family: "mainFont", sans-serif !important;
}
</style>
<!-- styles -->
<link rel="stylesheet" href="{{shared('Material-Icons/icon.css')}}">
<link rel="stylesheet" href="{{shared('material/material.min.css')}}">
<link rel="stylesheet" href="{{shared('iziToast/iziToast.min.css')}}">
<!-- scripts -->
<script src="{{shared('js/jquery3.js')}}"></script>
<script src="{{shared('js/vue.min.js')}}"></script>
{% block head %}{% endblock %}

</head>

<body style="direction:{{__('info.direction')}}">

<!-- main content -->
{% block content %}{% endblock %}
<!-- main scripts-->
<script src="{{shared('iziToast/iziToast.min.js')}}"></script>
<script src="{{shared('material/material.min.js')}}"></script>
{% block scripts %}{% endblock %}

</body>

</html>

فایل‌های زبان محلی (resources/locales/en/)

فایل‌های info.ts, main.ts, و msgs.ts را همانطور که در کد CLI نشان داده شده ایجاد کنید.

۷. نصب وابستگی‌ها و اجرا

pnpm install
pnpm run dev

مراحل بعدی

پس از ایجاد ساختار پایه:

  1. متادیتای اپلیکیشن را در app.ts سفارشی کنید
  2. مدل‌ها، APIها و نماهای خود را اضافه کنید
  3. مسیرها را پیکربندی کنید
  4. رشته‌های محلی‌سازی را اضافه کنید
  5. منطق کسب‌وکار خود را پیاده‌سازی کنید

این رویکرد دستی کنترل کامل بر هر فایل را به شما می‌دهد و امکان سفارشی‌سازی آسان در طول فرآیند ایجاد را فراهم می‌کند.