create-pactor-app 1.20.0
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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/cli.js +220 -0
- package/package.json +47 -0
- package/templates/next-app/README.md +112 -0
- package/templates/next-app/app/[[...slug]]/page.tsx +7 -0
- package/templates/next-app/app/api/auth/login/route.ts +32 -0
- package/templates/next-app/app/api/auth/logout/route.ts +22 -0
- package/templates/next-app/app/api/pages/[id]/route.ts +40 -0
- package/templates/next-app/app/api/sitemap/route.ts +21 -0
- package/templates/next-app/app/favicon.ico +0 -0
- package/templates/next-app/app/globals.css +12 -0
- package/templates/next-app/app/layout.tsx +28 -0
- package/templates/next-app/eslint.config.mjs +18 -0
- package/templates/next-app/package.json +30 -0
- package/templates/next-app/postcss.config.mjs +7 -0
- package/templates/next-app/public/file.svg +1 -0
- package/templates/next-app/public/globe.svg +1 -0
- package/templates/next-app/public/next.svg +1 -0
- package/templates/next-app/public/vercel.svg +1 -0
- package/templates/next-app/public/window.svg +1 -0
- package/templates/next-app/src/pactor/App.tsx +47 -0
- package/templates/next-app/src/pactor/config.ts +17 -0
- package/templates/next-app/src/server/accounts.ts +63 -0
- package/templates/next-app/src/server/cors.ts +34 -0
- package/templates/next-app/src/server/sitemap.ts +558 -0
- package/templates/next-app/tsconfig.json +34 -0
- package/templates/spa/README.md +97 -0
- package/templates/spa/index.html +13 -0
- package/templates/spa/package.json +29 -0
- package/templates/spa/src/main.tsx +12 -0
- package/templates/spa/src/pactor/App.tsx +27 -0
- package/templates/spa/src/pactor/config.ts +18 -0
- package/templates/spa/tsconfig.json +19 -0
- package/templates/spa/vite.config.ts +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
4
|
+
import { Suspense } from "react";
|
|
5
|
+
import { SitemapApp } from "pactor-app";
|
|
6
|
+
|
|
7
|
+
import { apiBaseUrl, sitemapSources } from "./config";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 动态页面应用(design/dynamic-page.md):框架 SitemapApp + 宿主接线。
|
|
11
|
+
* 路由解析 / 访问守卫 / DSL 布局(PageOutlet + SitemapMenu)/ 页面加载 /
|
|
12
|
+
* session.context 桥 / 缺省动作(app.reload / service.call)全部由
|
|
13
|
+
* @pactor-app/app 内置;此处只提供:sitemap 源、宿主路由
|
|
14
|
+
* (pathname / navigate)与 HTTP 钩子。
|
|
15
|
+
*
|
|
16
|
+
* 登录 / 登出是普通 HTTP 调用,由 DSL 中的内置 request 动作直接表达
|
|
17
|
+
* (见 src/server/sitemap.ts 下发的登录页 / 布局 DSL),无需客户端注册服务。
|
|
18
|
+
*
|
|
19
|
+
* 挂载在 app/layout.tsx(Next layout 跨导航常驻,切换导航只刷新内容区)。
|
|
20
|
+
*
|
|
21
|
+
* HTTP 钩子用框架默认实现 createHttpHooks:{ code, data, message } 拆包;
|
|
22
|
+
* 后端包裹协议不同 / 需公共头注入时传 options,需要代理改写时传
|
|
23
|
+
* proxyPrefix,或自行实现 PactorHttpHooks。
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export function PactorApp() {
|
|
27
|
+
return (
|
|
28
|
+
<Suspense fallback={null}>
|
|
29
|
+
<PactorAppInner />
|
|
30
|
+
</Suspense>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function PactorAppInner() {
|
|
35
|
+
const pathname = usePathname();
|
|
36
|
+
const searchParams = useSearchParams();
|
|
37
|
+
const router = useRouter();
|
|
38
|
+
return (
|
|
39
|
+
<SitemapApp
|
|
40
|
+
sources={sitemapSources}
|
|
41
|
+
baseUrl={apiBaseUrl}
|
|
42
|
+
pathname={pathname}
|
|
43
|
+
query={Object.fromEntries(searchParams.entries())}
|
|
44
|
+
navigate={(to) => router.push(to)}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 入口配置(design/dynamic-page.md):开发者在此配置一个或多个
|
|
3
|
+
* 返回 sitemap 对象的接口 URL。多源时 pages / menus / layouts 按键合并
|
|
4
|
+
* (id / path 冲突后源覆盖前源并告警),app / user / permissions / auth
|
|
5
|
+
* 取第一个源。
|
|
6
|
+
*
|
|
7
|
+
* 外部接口:sources / pageEndpoint 只写相对路径,由 apiBaseUrl 补全为
|
|
8
|
+
* 外部绝对地址;服务端已开通 CORS(cookie 会话见 src/server/cors.ts),
|
|
9
|
+
* 浏览器直连,无需项目内代理。
|
|
10
|
+
*/
|
|
11
|
+
export const sitemapSources: string[] = ["/api/sitemap"];
|
|
12
|
+
|
|
13
|
+
/** 外部接口基准地址(SitemapApp baseUrl):相对接口路径以此为基准补全 */
|
|
14
|
+
export const apiBaseUrl = "http://localhost:9011";
|
|
15
|
+
|
|
16
|
+
/** 懒加载页面端点(pages[].pageDsl 缺省时按 id 获取) */
|
|
17
|
+
export const pageEndpoint = (pageId: string): string => `/api/pages/${pageId}`;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 演示账号(mock):真实项目由后端用户体系替换。
|
|
3
|
+
* 会话经 httpOnly cookie 传递(pactor_demo_session),sitemap 端点据此裁剪内容。
|
|
4
|
+
*/
|
|
5
|
+
export interface DemoUser {
|
|
6
|
+
id: string;
|
|
7
|
+
username: string;
|
|
8
|
+
name: string;
|
|
9
|
+
roles: string[];
|
|
10
|
+
permissions: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const DEMO_ACCOUNTS: (DemoUser & { password: string })[] = [
|
|
14
|
+
{
|
|
15
|
+
id: "u-1",
|
|
16
|
+
username: "admin",
|
|
17
|
+
password: "pactor123",
|
|
18
|
+
name: "演示管理员",
|
|
19
|
+
roles: ["admin"],
|
|
20
|
+
permissions: ["report:view"],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "u-2",
|
|
24
|
+
username: "guest",
|
|
25
|
+
password: "guest123",
|
|
26
|
+
name: "访客",
|
|
27
|
+
roles: ["guest"],
|
|
28
|
+
permissions: [],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export const SESSION_COOKIE = "pactor_demo_session";
|
|
33
|
+
|
|
34
|
+
function toDemoUser(account: (typeof DEMO_ACCOUNTS)[number]): DemoUser {
|
|
35
|
+
return {
|
|
36
|
+
id: account.id,
|
|
37
|
+
username: account.username,
|
|
38
|
+
name: account.name,
|
|
39
|
+
roles: account.roles,
|
|
40
|
+
permissions: account.permissions,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function findAccount(
|
|
45
|
+
username: string,
|
|
46
|
+
password: string,
|
|
47
|
+
): DemoUser | null {
|
|
48
|
+
const hit = DEMO_ACCOUNTS.find(
|
|
49
|
+
(a) => a.username === username && a.password === password,
|
|
50
|
+
);
|
|
51
|
+
return hit ? toDemoUser(hit) : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** 会话 cookie 值 = username(演示环境明文,生产换成签名 token / session id) */
|
|
55
|
+
export function findUserBySession(
|
|
56
|
+
cookieValue: string | undefined,
|
|
57
|
+
): DemoUser | null {
|
|
58
|
+
if (!cookieValue) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const hit = DEMO_ACCOUNTS.find((a) => a.username === cookieValue);
|
|
62
|
+
return hit ? toDemoUser(hit) : null;
|
|
63
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CORS(演示环境放开跨源):真实项目由后端框架 / 网关统一配置。
|
|
5
|
+
* 会话经 cookie 传递——Allow-Origin 必须回显请求源(带凭据时不能用 *)
|
|
6
|
+
* 且带 Allow-Credentials;POST JSON 会触发预检(OPTIONS)。
|
|
7
|
+
*/
|
|
8
|
+
export function corsHeaders(request: Request): Record<string, string> {
|
|
9
|
+
const origin = request.headers.get("origin");
|
|
10
|
+
return {
|
|
11
|
+
"access-control-allow-origin": origin ?? "*",
|
|
12
|
+
"access-control-allow-credentials": "true",
|
|
13
|
+
"access-control-allow-methods": "GET, POST, OPTIONS",
|
|
14
|
+
"access-control-allow-headers": "content-type, authorization",
|
|
15
|
+
vary: "origin",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function corsJson(
|
|
20
|
+
request: Request,
|
|
21
|
+
data: unknown,
|
|
22
|
+
init?: ResponseInit,
|
|
23
|
+
): NextResponse {
|
|
24
|
+
const response = NextResponse.json(data, init);
|
|
25
|
+
for (const [key, value] of Object.entries(corsHeaders(request))) {
|
|
26
|
+
response.headers.set(key, value);
|
|
27
|
+
}
|
|
28
|
+
return response;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 预检响应(路由中 `export const OPTIONS = corsPreflight`) */
|
|
32
|
+
export function corsPreflight(request: Request): Response {
|
|
33
|
+
return new Response(null, { status: 204, headers: corsHeaders(request) });
|
|
34
|
+
}
|