@longzai-intelligence-tenant/elysia-core-plugin 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +50 -0
- package/dist/index.js +1 -0
- package/package.json +54 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { RouteGroup } from "@longzai-intelligence-tenant/sdk";
|
|
2
|
+
import Elysia from "elysia";
|
|
3
|
+
|
|
4
|
+
//#region src/preset.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 创建租户预设应用
|
|
7
|
+
*
|
|
8
|
+
* 内部使用内存适配器,零配置,开箱即用。
|
|
9
|
+
* 不暴露任何 domain 层类型。
|
|
10
|
+
*/
|
|
11
|
+
declare function createTenantPreset(): (app: Elysia) => Elysia<"", {
|
|
12
|
+
decorator: {};
|
|
13
|
+
store: {};
|
|
14
|
+
derive: {};
|
|
15
|
+
resolve: {};
|
|
16
|
+
}, {
|
|
17
|
+
typebox: {};
|
|
18
|
+
error: {};
|
|
19
|
+
}, {
|
|
20
|
+
schema: {};
|
|
21
|
+
standaloneSchema: {};
|
|
22
|
+
macro: {};
|
|
23
|
+
macroFn: {};
|
|
24
|
+
parser: {};
|
|
25
|
+
response: {};
|
|
26
|
+
}, {}, {
|
|
27
|
+
derive: {};
|
|
28
|
+
resolve: {};
|
|
29
|
+
schema: {};
|
|
30
|
+
standaloneSchema: {};
|
|
31
|
+
response: {};
|
|
32
|
+
}, {
|
|
33
|
+
derive: {};
|
|
34
|
+
resolve: {};
|
|
35
|
+
schema: {};
|
|
36
|
+
standaloneSchema: {};
|
|
37
|
+
response: {};
|
|
38
|
+
}>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/mount.d.ts
|
|
41
|
+
type PortImplementation = Record<string, (...args: unknown[]) => Promise<unknown>>;
|
|
42
|
+
/**
|
|
43
|
+
* 将 SDK 路由组挂载到 Elysia 实例
|
|
44
|
+
*
|
|
45
|
+
* 读取 SDK 的 defineRouteGroup 路由定义,将每个路由挂载到 Elysia。
|
|
46
|
+
* 框架适配层不自行定义路由,仅负责挂载和框架适配。
|
|
47
|
+
*/
|
|
48
|
+
declare function mountRoutes(app: Elysia, routeGroup: RouteGroup, implementations: PortImplementation): Elysia;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { createTenantPreset, mountRoutes };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{membershipApi as e,quotaApi as t,subscriptionApi as n,tenantApi as r}from"@longzai-intelligence-tenant/sdk";import{failure as i,success as a}from"@longzai-intelligence/shared-kernel";function o(e,t,n){for(let[r,i]of Object.entries(t.routes)){let a=`/${t.prefix}${i.path}`,o=n[r];o&&(i.method===`GET`?e.get(a,async e=>o(e.params,e.query)):i.method===`POST`?e.post(a,async e=>o(e.body,e.params)):i.method===`PATCH`?e.patch(a,async e=>o(e.body,e.params)):i.method===`DELETE`?e.delete(a,async e=>o(e.params)):i.method===`PUT`&&e.put(a,async e=>o(e.body,e.params)))}return e}var s=class{store=new Map;slugIndex=new Map;async findById(e){return a(this.store.get(e)??null)}async findBySlug(e){let t=this.slugIndex.get(e);return a(t?this.store.get(t)??null:null)}async findByOwnerId(e){return a(Array.from(this.store.values()).filter(t=>t.ownerId===e))}async findAll(e){let t=Array.from(this.store.values());if(e?.status&&(t=t.filter(t=>t.status===e.status)),e?.search){let n=e.search.toLowerCase();t=t.filter(e=>e.name.toLowerCase().includes(n)||e.slug.toLowerCase().includes(n))}let n=t.length,r=e?.page??1,i=e?.pageSize??20,o=Math.max(1,Math.ceil(n/i)),s=(r-1)*i;return a({items:t.slice(s,s+i).map(e=>({id:e.id,name:e.name,slug:e.slug,status:e.status,plan:e.plan,ownerId:e.ownerId,createdAt:e.createdAt})),total:n,page:r,pageSize:i,totalPages:o})}async create(e){let t=crypto.randomUUID(),n=new Date().toISOString();if(this.slugIndex.has(e.slug))return i({name:`TenantSlugDuplicateError`,message:`租户标识 ${e.slug} 已存在`,slug:e.slug});let r={id:t,name:e.name,slug:e.slug,status:`active`,plan:e.plan??null,ownerId:e.ownerId,maxMembers:e.maxMembers??10,settings:e.settings??{},createdAt:n,updatedAt:n};return this.store.set(t,r),this.slugIndex.set(e.slug,t),a(r)}async update(e,t){let n=this.store.get(e);if(!n)return i({name:`TenantNotFoundError`,message:`租户 ${e} 未找到`,tenantId:e});let r=new Date().toISOString(),o={...n,name:t.name??n.name,plan:t.plan??n.plan,maxMembers:t.maxMembers??n.maxMembers,settings:t.settings??n.settings,updatedAt:r};return this.store.set(e,o),a(o)}async delete(e){let t=this.store.get(e);return t&&this.slugIndex.delete(t.slug),this.store.delete(e),a(void 0)}},c=class{currentTenant=null;getCurrentTenant(){return this.currentTenant}setTenant(e){this.currentTenant=e}async validateTenant(e){return this.currentTenant?this.currentTenant.id===e?{valid:!0,tenant:this.currentTenant}:{valid:!1,error:`租户 ID ${e} 与当前租户不匹配`}:{valid:!1,error:`未设置当前租户`}}clearTenant(){this.currentTenant=null}},l=class{tenantStore=new Map;slugIndex=new Map;registerTenant(e){this.tenantStore.set(e.id,e),e.slug&&this.slugIndex.set(e.slug,e)}async resolveFromHeader(e){let t=e[`x-tenant-id`];if(t)return this.tenantStore.get(t)??null;let n=e[`x-tenant-slug`];return n?this.slugIndex.get(n)??null:null}async resolveFromSubdomain(e){let t=e.split(`.`);if(t.length>=2){let e=t[0];return this.slugIndex.get(e)??null}return null}async resolveFromToken(e){if(e.startsWith(`tenant-`)){let t=e.slice(7);return this.tenantStore.get(t)??null}return null}},u=class{store=new Map;async findById(e){return a(this.store.get(e)??null)}async findByTenantId(e){return a(Array.from(this.store.values()).filter(t=>t.tenantId===e))}async findByUserId(e){return a(Array.from(this.store.values()).filter(t=>t.userId===e))}async findByTenantAndUser(e,t){return a(Array.from(this.store.values()).find(n=>n.tenantId===e&&n.userId===t)??null)}async add(e){let t=crypto.randomUUID(),n=new Date().toISOString(),r={id:t,tenantId:e.tenantId,userId:e.userId,role:e.role,status:`active`,joinedAt:n};return this.store.set(t,r),a(r)}async changeRole(e,t){let n=this.store.get(e);if(!n)return i({name:`MemberNotFoundError`,message:`成员 ${e} 未找到`,memberId:e});let r={...n,role:t.role};return this.store.set(e,r),a(r)}async delete(e){return this.store.delete(e),a(void 0)}},d=class{store=new Map;codeIndex=new Map;async findById(e){return a(this.store.get(e)??null)}async findByTenantId(e){return a(Array.from(this.store.values()).filter(t=>t.tenantId===e))}async findByCode(e){let t=this.codeIndex.get(e);return a(t?this.store.get(t)??null:null)}async create(e){let t=crypto.randomUUID(),n=new Date().toISOString(),r=new Date(Date.now()+7*864e5).toISOString(),i={id:t,tenantId:e.tenantId,inviterId:e.inviterId,email:e.email,role:e.role,code:crypto.randomUUID().slice(0,8).toUpperCase(),status:`pending`,createdAt:n,expiresAt:r};return this.store.set(t,i),this.codeIndex.set(i.code,t),a(i)}async accept(e){let t=this.codeIndex.get(e.code);if(!t)return i({name:`InvitationNotFoundError`,message:`邀请码 ${e.code} 未找到`,code:e.code});let n=this.store.get(t);if(!n)return i({name:`InvitationNotFoundError`,message:`邀请 ${t} 未找到`,invitationId:t});let r={...n,status:`accepted`,acceptedAt:new Date().toISOString(),acceptedBy:e.userId};return this.store.set(t,r),a(r)}async delete(e){let t=this.store.get(e);return t&&this.codeIndex.delete(t.code),this.store.delete(e),a(void 0)}},f=class{store=new Map;tenantIndex=new Map;async findById(e){return a(this.store.get(e)??null)}async findByTenantId(e){let t=this.tenantIndex.get(e);return a(t?this.store.get(t)??null:null)}async subscribe(e){let t=crypto.randomUUID(),n=new Date().toISOString(),r=e.trialDays?new Date(Date.now()+e.trialDays*864e5).toISOString():null,i={id:t,tenantId:e.tenantId,planId:e.planId,status:e.trialDays?`trial`:`active`,startDate:n,endDate:null,trialEndAt:r};return this.store.set(t,i),this.tenantIndex.set(e.tenantId,t),a(i)}async delete(e){let t=this.store.get(e);return t&&this.tenantIndex.delete(t.tenantId),this.store.delete(e),a(void 0)}},p=class{store=new Map;async findById(e){return a(this.store.get(e)??null)}async findAll(){return a(Array.from(this.store.values()))}async create(e){let t=crypto.randomUUID(),n=new Date().toISOString(),r={id:t,name:e.name,description:e.description??null,features:e.features??[],limits:e.limits??{},price:e.price,billingCycle:e.billingCycle,status:`active`,createdAt:n};return this.store.set(t,r),a(r)}async delete(e){return this.store.delete(e),a(void 0)}},m=class{store=new Map;async findById(e){return a(this.store.get(e)??null)}async findByTenantId(e){return a(Array.from(this.store.values()).filter(t=>t.tenantId===e))}async findByTenantAndType(e,t){return a(Array.from(this.store.values()).find(n=>n.tenantId===e&&n.resourceType===t)??null)}async check(e,t){let n=Array.from(this.store.values()).find(n=>n.tenantId===e&&n.resourceType===t);return a(n?{allowed:n.used<n.limit,resourceType:n.resourceType,current:n.used,limit:n.limit}:{allowed:!1,resourceType:t,current:0,limit:0})}async adjust(e){let t=this.store.get(e.id);if(!t)return i({name:`QuotaNotFoundError`,message:`配额 ${e.id} 未找到`,quotaId:e.id});let n={...t,limit:e.limit??t.limit,used:e.used??t.used};return this.store.set(e.id,n),a(n)}async delete(e){return this.store.delete(e),a(void 0)}};function h(){let i=new s,a=new c,h=new l,g=new u,_=new d,v=new p,y=new f,b=new m;return s=>(s.derive(async({request:e})=>{let t=null,n={};if(e.headers.forEach((e,t)=>{n[t]=e}),t=await h.resolveFromHeader(n),!t){let n=e.headers.get(`host`);n&&(t=await h.resolveFromSubdomain(n))}if(!t){let n=e.headers.get(`authorization`);if(n){let e=n.startsWith(`Bearer `)?n.slice(7):n;t=await h.resolveFromToken(e)}}return t&&a.setTenant(t),{tenant:t}}).as(`scoped`),s=o(s,r,{getTenant:e=>i.findById(e.id),listTenants:(e,t)=>i.findAll(t),createTenant:e=>i.create(e),updateTenant:(e,t)=>i.update(t.id,e),deleteTenant:e=>i.delete(e.id),getTenantBySlug:e=>i.findBySlug(e.slug),validateTenant:e=>a.validateTenant(e.id),resolveFromHeader:e=>{let t=e;return h.resolveFromHeader(t)},resolveFromSubdomain:e=>{let t=e;return h.resolveFromSubdomain(t.host)},resolveFromToken:e=>{let t=e;return h.resolveFromToken(t.token)}}),s=o(s,e,{getMember:e=>g.findById(e.id),listMembersByTenant:(e,t)=>g.findByTenantId(e.tenantId),listMembersByUser:e=>g.findByUserId(e.userId),getMemberByTenantAndUser:e=>g.findByTenantAndUser(e.tenantId,e.userId),addMember:(e,t)=>g.add({...e,tenantId:t.tenantId}),deleteMember:e=>g.delete(e.id),getInvitation:e=>_.findById(e.id),listInvitationsByTenant:e=>_.findByTenantId(e.tenantId),getInvitationByCode:e=>_.findByCode(e.code),createInvitation:(e,t)=>_.create({...e,tenantId:t.tenantId}),deleteInvitation:e=>_.delete(e.id)}),s=o(s,n,{getPlan:e=>v.findById(e.id),listPlans:()=>v.findAll(),createPlan:e=>v.create(e),deletePlan:e=>v.delete(e.id),getSubscription:e=>y.findById(e.id),getSubscriptionByTenant:e=>y.findByTenantId(e.tenantId),subscribe:(e,t)=>y.subscribe({...e,tenantId:t.tenantId}),cancelSubscription:e=>y.delete(e.id)}),s=o(s,t,{getQuota:e=>b.findById(e.id),listQuotasByTenant:e=>b.findByTenantId(e.tenantId),getQuotaByTenantAndType:e=>b.findByTenantAndType(e.tenantId,e.resourceType),checkQuota:(e,t)=>b.check(t.tenantId,e.resourceType),adjustQuota:e=>b.adjust(e),deleteQuota:e=>b.delete(e.id)}),s)}export{h as createTenantPreset,o as mountRoutes};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-tenant/elysia-core-plugin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsgo --build tsconfig/build.json && resolve-aliases -p tsconfig/build.json",
|
|
20
|
+
"build:prod": "NODE_ENV=production tsdown",
|
|
21
|
+
"prepublishOnly": "bun run build:prod",
|
|
22
|
+
"typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
|
|
23
|
+
"typecheck:app": "tsgo --noEmit -p tsconfig/app.json",
|
|
24
|
+
"typecheck:node": "tsgo --noEmit -p tsconfig/node.json",
|
|
25
|
+
"typecheck:test": "tsgo --noEmit -p tsconfig/test.json",
|
|
26
|
+
"lint": "oxlint src && oxfmt --check src",
|
|
27
|
+
"lint:fix": "oxlint --fix src && oxfmt src",
|
|
28
|
+
"test": "bun test",
|
|
29
|
+
"test:watch": "bun test --watch",
|
|
30
|
+
"test:coverage": "bun test --coverage",
|
|
31
|
+
"clean": "rm -rf dist out .cache"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@longzai-intelligence-tenant/sdk": "0.0.1",
|
|
35
|
+
"@longzai-intelligence-tenant/core": "0.0.1",
|
|
36
|
+
"@longzai-intelligence/shared-kernel": "^0.1.4",
|
|
37
|
+
"@longzai-intelligence/error": "^0.0.5",
|
|
38
|
+
"zod": "^4.4.3"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"elysia": "^1.3"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"elysia": "^1.3",
|
|
45
|
+
"@longzai-intelligence-tenant/tenant-contract": "0.0.1",
|
|
46
|
+
"@longzai-intelligence-tenant/tenant-implementation": "0.0.1",
|
|
47
|
+
"@longzai-intelligence-tenant/membership-contract": "0.0.1",
|
|
48
|
+
"@longzai-intelligence-tenant/membership-implementation": "0.0.1",
|
|
49
|
+
"@longzai-intelligence-tenant/subscription-contract": "0.0.1",
|
|
50
|
+
"@longzai-intelligence-tenant/subscription-implementation": "0.0.1",
|
|
51
|
+
"@longzai-intelligence-tenant/quota-contract": "0.0.1",
|
|
52
|
+
"@longzai-intelligence-tenant/quota-implementation": "0.0.1"
|
|
53
|
+
}
|
|
54
|
+
}
|