feat: update README with installation command and add debug logs in deploy command

- Added installation command for the CLI tool in README.
- Enhanced deploy command with debug logging for upload results and query app version.
- Integrated useKey for fetching KEVISUAL_TOKEN in get-config module.
- Added debug logging in queryAppVersion for better traceability.
- Updated temp.md with new dependency and example command for deployment.
This commit is contained in:
2026-01-17 23:28:38 +08:00
parent 5395449751
commit 91d4fed474
13 changed files with 321 additions and 2039 deletions

View File

@@ -31,7 +31,7 @@ const authFilter = async (req: http.IncomingMessage, res: http.ServerResponse) =
const auth = _assistantConfig?.auth || {}; const auth = _assistantConfig?.auth || {};
const share = auth.share || 'protected'; const share = auth.share || 'protected';
const noAdmin = !auth.username; const noAdmin = !auth.username;
if (noAdmin) return false; if (noAdmin) return { code: 500, message: '没有管理员' };
const admin = auth.username; const admin = auth.username;
const admins = auth.admin || []; const admins = auth.admin || [];
if (admin) { if (admin) {
@@ -41,42 +41,43 @@ const authFilter = async (req: http.IncomingMessage, res: http.ServerResponse) =
const pathname = decodeURIComponent(url.pathname); const pathname = decodeURIComponent(url.pathname);
// 放开 / // 放开 /
if (pathname === '/' || pathname === '/favicon.ico') { if (pathname === '/' || pathname === '/favicon.ico') {
return false; return { code: 200, message: '允许访问根路径' };
} }
// 放开首页 // 放开首页
if (pathname.startsWith('/root/home') || pathname === '/root/cli') { if (pathname.startsWith('/root/home') || pathname === '/root/cli/docs/') {
return false; return { code: 200, message: '允许访问首页' };
} }
// 放开api 以 /api /v1, /client, /serve 开头的请求 // 放开api 以 /api /v1, /client, /serve 开头的请求
const openApiPaths = ['/api', '/v1', '/client', '/serve']; const openApiPaths = ['/api', '/v1', '/client', '/serve', '/proxy'];
for (const openPath of openApiPaths) { for (const openPath of openApiPaths) {
if (pathname.startsWith(openPath)) { if (pathname.startsWith(openPath)) {
return false; return { code: 200, message: '允许访问API' };
} }
} }
if (share === 'public') { if (share === 'public') {
return false; return { code: 200, message: '公开模式允许访问' };
} }
const { token } = await getToken(req) const { token } = await getToken(req)
if (!token) { if (!token) {
// no token 转到登录页面 // no token 转到登录页面
res.writeHead(302, { Location: `/root/home/` }); res.writeHead(302, { Location: `/root/home/` });
res.end(); res.end();
return false; return { code: 500, message: '未登录' };
} }
const tokenUser = await getTokenUserCache(token); const tokenUser = await getTokenUserCache(token);
console.log('authFilter tokenUser', tokenUser, token);
if (share === 'protected' && tokenUser?.code === 200) { if (share === 'protected' && tokenUser?.code === 200) {
return false; return { code: 200, message: '受保护模式已登录允许访问' };
} }
if (share === 'private') { if (share === 'private') {
if (tokenUser?.code === 200) { if (tokenUser?.code === 200) {
const username = tokenUser?.data?.username; const username = tokenUser?.data?.username;
if (admins.includes(username)) { if (admins.includes(username)) {
return false; return { code: 200, message: '私有模式管理员允许访问' };
} }
} }
} }
return true; return { code: 500, message: '没有权限访问' };
} }
export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResponse) => { export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResponse) => {
const _assistantConfig = assistantConfig.getCacheAssistantConfig(); const _assistantConfig = assistantConfig.getCacheAssistantConfig();
@@ -180,7 +181,8 @@ export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResp
}); });
} }
const filter = await authFilter(req, res); const filter = await authFilter(req, res);
if (filter) { if (filter.code !== 200) {
console.log('auth filter deny', filter);
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
return res.end(renderNoAuthAndLogin('Not Authorized Proxy')); return res.end(renderNoAuthAndLogin('Not Authorized Proxy'));
} }

View File

@@ -19,6 +19,7 @@ let proxy = {
}; };
const basename = isDev ? undefined : `${pkgs.basename}`; const basename = isDev ? undefined : `${pkgs.basename}`;
console.log('Astro Config Basename:', basename);
export default defineConfig({ export default defineConfig({
base: basename, base: basename,
integrations: [ integrations: [

View File

@@ -8,10 +8,10 @@
"dev": "astro dev", "dev": "astro dev",
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"pub": "envision deploy ./dist -k cli -v 0.0.3 -u -y y", "pub": "envision deploy ./dist -k cli -v 0.0.4 -u -y y",
"slide:dev": "slidev --open slides/index.md", "slide:dev": "slidev --open slides/index.md",
"slide:build": "slidev build slides/index.md --base /root/cli-slide/", "slide:build": "slidev build slides/index.md --base /root/cli-slide/",
"slide:pub": "envision deploy ./slides/dist -k cli-slide -v 0.0.3 -u", "slide:pub": "envision deploy ./slides/dist -k cli-slide -v 0.0.4 -u",
"ui": "pnpm dlx shadcn@latest add " "ui": "pnpm dlx shadcn@latest add "
}, },
"keywords": [], "keywords": [],
@@ -21,23 +21,23 @@
"dependencies": { "dependencies": {
"@astrojs/mdx": "^4.3.13", "@astrojs/mdx": "^4.3.13",
"@astrojs/react": "^4.4.2", "@astrojs/react": "^4.4.2",
"@astrojs/sitemap": "^3.6.0", "@astrojs/sitemap": "^3.7.0",
"@astrojs/vue": "^5.1.3", "@astrojs/vue": "^5.1.4",
"@kevisual/api": "^0.0.5", "@kevisual/api": "^0.0.17",
"@kevisual/context": "^0.0.4", "@kevisual/context": "^0.0.4",
"@kevisual/kv-code": "^0.0.4", "@kevisual/kv-code": "^0.0.4",
"@kevisual/query": "^0.0.33", "@kevisual/query": "^0.0.35",
"@kevisual/query-login": "^0.0.7", "@kevisual/query-login": "^0.0.7",
"@kevisual/registry": "^0.0.1", "@kevisual/registry": "^0.0.1",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@uiw/react-md-editor": "^4.0.11", "@uiw/react-md-editor": "^4.0.11",
"antd": "^6.1.1", "antd": "^6.2.0",
"astro": "^5.16.6", "astro": "^5.16.11",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"es-toolkit": "^1.43.0", "es-toolkit": "^1.44.0",
"github-markdown-css": "^5.8.1", "github-markdown-css": "^5.8.1",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",
@@ -49,20 +49,20 @@
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"vue": "^3.5.26", "vue": "^3.5.26",
"zustand": "^5.0.9" "zustand": "^5.0.10"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@kevisual/types": "^0.0.10", "@kevisual/types": "^0.0.11",
"@types/react": "^19.2.7", "@types/react": "^19.2.8",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"tw-animate-css": "^1.4.0" "tw-animate-css": "^1.4.0"
}, },
"packageManager": "pnpm@10.26.1", "packageManager": "pnpm@10.28.0",
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@tailwindcss/oxide", "@tailwindcss/oxide",
"esbuild", "esbuild",

View File

@@ -1,4 +1,4 @@
import { query, queryLogin } from '@/modules/query'; import { clientQuery, queryLogin } from '@/modules/query';
import { create } from 'zustand'; import { create } from 'zustand';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
type SettingState = { type SettingState = {
@@ -12,7 +12,7 @@ export const useStore = create<SettingState>((set => ({
username: undefined, username: undefined,
config: undefined, config: undefined,
initAdmin: async () => { initAdmin: async () => {
const res = await query.post({ const res = await clientQuery.post({
path: 'config' path: 'config'
}) })
console.log('initAdmin', res); console.log('initAdmin', res);
@@ -25,7 +25,7 @@ export const useStore = create<SettingState>((set => ({
} }
}, },
login: async (username: string, password: string) => { login: async (username: string, password: string) => {
const res = await query.post({ const res = await clientQuery.post({
path: 'admin', path: 'admin',
key: 'login', key: 'login',
username, username,
@@ -40,7 +40,7 @@ export const useStore = create<SettingState>((set => ({
return res; return res;
}, },
saveConfig: async (config: any) => { saveConfig: async (config: any) => {
const res = await query.post({ const res = await clientQuery.post({
path: 'config', path: 'config',
key: 'set', key: 'set',
data: config data: config

View File

@@ -14,6 +14,10 @@ export const query = new QueryClient({
url: getUrl() url: getUrl()
}); });
export const clientQuery = new QueryClient({
url: '/client/router'
});
export const remoteQuery = new Query({ export const remoteQuery = new Query({
url: '/api/router' url: '/api/router'
}); });

View File

@@ -10,13 +10,13 @@ import Html from '@/components/html.astro';
</div> </div>
<div class="card-grid"> <div class="card-grid">
<a href="./docs/" class="card"> <a href={"/root/cli/docs/"} class="card">
<div class="card-icon">📚</div> <div class="card-icon">📚</div>
<h2>文档中心</h2> <h2>文档中心</h2>
<p>查看完整的使用文档和API参考</p> <p>查看完整的使用文档和API参考</p>
</a> </a>
<a href="./settings/" class="card"> <a href={"/root/cli/settings/"} class="card">
<div class="card-icon">⚙️</div> <div class="card-icon">⚙️</div>
<h2>设置中心</h2> <h2>设置中心</h2>
<p>配置和管理您的应用设置</p> <p>配置和管理您的应用设置</p>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@kevisual/cli", "name": "@kevisual/cli",
"version": "0.0.85", "version": "0.0.87",
"description": "envision 命令行工具", "description": "envision 命令行工具",
"type": "module", "type": "module",
"basename": "/root/cli", "basename": "/root/cli",
@@ -44,20 +44,18 @@
"@inquirer/prompts": "^8.2.0", "@inquirer/prompts": "^8.2.0",
"@kevisual/app": "^0.0.2", "@kevisual/app": "^0.0.2",
"@kevisual/context": "^0.0.4", "@kevisual/context": "^0.0.4",
"@kevisual/hot-api": "^0.0.3",
"@kevisual/use-config": "^1.0.28", "@kevisual/use-config": "^1.0.28",
"@types/busboy": "^1.5.4", "@types/busboy": "^1.5.4",
"busboy": "^1.6.0", "busboy": "^1.6.0",
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"lowdb": "^7.0.1", "lowdb": "^7.0.1",
"lru-cache": "^11.2.4", "lru-cache": "^11.2.4",
"micromatch": "^4.0.8", "micromatch": "^4.0.8",
"pm2": "^6.0.14",
"semver": "^7.7.3", "semver": "^7.7.3",
"unstorage": "^1.17.4" "unstorage": "^1.17.4"
}, },
"devDependencies": { "devDependencies": {
"pm2": "^6.0.14",
"@kevisual/dts": "^0.0.3", "@kevisual/dts": "^0.0.3",
"@kevisual/load": "^0.0.6", "@kevisual/load": "^0.0.6",
"@kevisual/logger": "^0.0.4", "@kevisual/logger": "^0.0.4",

2264
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1,5 @@
# 一个简单的cli工具 # 一个简单的cli工具
```
npm i @kevisual/cli -g --registry=https://npm.cnb.cool/kevisual/registry/-/packages/
```

View File

@@ -111,6 +111,7 @@ const command = new Command('deploy')
} }
const uploadDirectory = isDirectory ? directory : path.dirname(directory); const uploadDirectory = isDirectory ? directory : path.dirname(directory);
const res = await uploadFiles(_relativeFiles, uploadDirectory, { key, version, username: org, noCheckAppFiles: !noCheck, directory: options.directory }); const res = await uploadFiles(_relativeFiles, uploadDirectory, { key, version, username: org, noCheckAppFiles: !noCheck, directory: options.directory });
logger.debug('upload res', res);
if (res?.code === 200) { if (res?.code === 200) {
res.data?.upload?.map?.((d) => { res.data?.upload?.map?.((d) => {
console.log(chalk.green('uploaded file', d?.name, d?.path)); console.log(chalk.green('uploaded file', d?.name, d?.path));
@@ -119,6 +120,7 @@ const command = new Command('deploy')
key: key, key: key,
version: version, version: version,
}); });
logger.debug('queryAppVersion res', res2);
if (res2.code !== 200) { if (res2.code !== 200) {
console.error(chalk.red('查询应用版本失败'), res2.message, key); console.error(chalk.red('查询应用版本失败'), res2.message, key);
return; return;

View File

@@ -1,6 +1,7 @@
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { useKey } from '@kevisual/use-config';
export const envisionPath = path.join(os.homedir(), '.config', 'envision'); export const envisionPath = path.join(os.homedir(), '.config', 'envision');
const configPath = path.join(os.homedir(), '.config', 'envision', 'config.json'); const configPath = path.join(os.homedir(), '.config', 'envision', 'config.json');
@@ -37,5 +38,8 @@ export const writeConfig = (config: Record<string, any>) => {
}; };
export const getEnvToken = () => { export const getEnvToken = () => {
return process.env.KEVISUAL_TOKEN || ''; const envTokne = useKey('KEVISUAL_TOKEN') || '';
console.log('getEnvToken', envTokne);
// return envTokne;
return '';
} }

View File

@@ -1,3 +1,4 @@
import { logger } from '@/module/logger.ts';
import { query } from '@/module/query.ts'; import { query } from '@/module/query.ts';
import { DataOpts } from '@kevisual/query'; import { DataOpts } from '@kevisual/query';
@@ -26,6 +27,7 @@ export const queryApp = async (params: QueryAppParams, opts?: any) => {
}; };
export const queryAppVersion = async (params: { key?: string; version?: string; id?: string }, opts?: DataOpts) => { export const queryAppVersion = async (params: { key?: string; version?: string; id?: string }, opts?: DataOpts) => {
logger.debug('queryAppVersion params', params, query.url);
return await query.post( return await query.post(
{ {
path: 'app', path: 'app',

View File

@@ -1 +1,4 @@
"@nut-tree-fork/nut-js": "^4.2.6", "@nut-tree-fork/nut-js": "^4.2.6",
"@kevisual/hot-api": "^0.0.3",
KEVISUAL_TOKEN="" LOG_LEVEL=DEBUG pnpm dev deploy ./cli-center/dist -k cli -v 0.0.4 -u -y y