Commit 66b62536394a4748a136dce10956c58cf080d8b0

Authored by xp.Huang
2 parents 137e30bf cdd94488

Merge branch 'ww' into 'main'

feat: 实现同源登录验证,增加命令行启动不同开发环境

See merge request yunteng/thingskit-view!2
... ... @@ -12,3 +12,6 @@ VITE_GLOB_APP_TITLE = GoView
12 12
13 13 # spa shortname
14 14 VITE_GLOB_APP_SHORT_NAME = GoView
  15 +
  16 +# iot platform proxy prefix
  17 +VITE_IOT_PLATFORM_PROXY_PREFIX = /large-designer/
... ...
... ... @@ -9,3 +9,6 @@ VITE_GLOB_API_URL_PREFIX = /yt
9 9
10 10 # 内容安全协议
11 11 VITE_GLOB_CONTENT_SECURITY_POLICY = false
  12 +
  13 +# 公共路径
  14 +VITE_GLOB_PUBLIC_PATH = /
... ...
  1 +# Proxy
  2 +VITE_GLOB_PROXY = [["/api", "http://222.180.200.114:48080/api"]]
  3 +
  4 +# Api prefix
  5 +VITE_GLOB_API_URL = /api
  6 +
  7 +#
  8 +VITE_GLOB_API_URL_PREFIX = /yt
  9 +
  10 +# 内容安全协议
  11 +VITE_GLOB_CONTENT_SECURITY_POLICY = false
... ...
... ... @@ -8,3 +8,6 @@ VITE_GLOB_API_URL_PREFIX = /yt
8 8
9 9 # 内容安全协议
10 10 VITE_GLOB_CONTENT_SECURITY_POLICY = true
  11 +
  12 +#
  13 +VITE_GLOB_PUBLIC_PATH = /
... ...
1 1 import { resolve } from "path"
  2 +import { ConfigEnv } from "vite"
2 3
3 4 export function parseEnv(env: ImportMetaEnv) {
4 5 const res: Record<string, any> = {}
... ... @@ -26,7 +27,7 @@ export function parseEnv(env: ImportMetaEnv) {
26 27 }
27 28
28 29 }
29   -
  30 +
30 31 return res
31 32 }
32 33
... ... @@ -37,3 +38,8 @@ export function getEnvConfig(match = 'VITE_GLOB_',) {
37 38 export function getRootPath(...dir: string[]) {
38 39 return resolve(process.cwd(), ...dir);
39 40 }
  41 +
  42 +
  43 +export function getPublicPath({ command, mode }: ConfigEnv, viteEnv: ViteEnv) {
  44 + return command === 'build' ? '/' : mode === 'independence' ? viteEnv.VITE_IOT_PLATFORM_PROXY_PREFIX: '/'
  45 +}
... ...
build/external/vite/plugins/globConfig/const.ts renamed from build/external/globConfig/const.ts
build/external/vite/plugins/globConfig/getGlobConfigName.ts renamed from build/external/globConfig/getGlobConfigName.ts
build/external/vite/plugins/globConfig/index.ts renamed from build/external/globConfig/index.ts
1   -import { getRootPath } from "../utils";
  1 +import { getRootPath } from "../../../utils";
2 2 import { writeFileSync } from "fs";
3 3 import { Plugin } from "vite";
4 4 import { GLOB_CONFIG_FILE_NAME } from "./const";
... ... @@ -10,7 +10,7 @@ export function GenerateBuildConfig(viteEnv: Record<string, any>) {
10 10 apply: 'build',
11 11 enforce: 'post',
12 12 closeBundle() {
13   -
  13 +
14 14 function createConfig({ config, configName, configFileName }: { configName: string, config: Record<string, any>, configFileName?: string } = { configName: '', config: {} }) {
15 15 try {
16 16 const windowConf = `window.${configName}`;
... ...
build/external/vite/plugins/html.ts renamed from build/external/vite/html.ts
... ... @@ -2,10 +2,10 @@ import { createHtmlPlugin } from 'vite-plugin-html'
2 2
3 3 const GLOB_CONFIG_FILE_NAME = '_app.config.js'
4 4
5   -export function configHtmlPlugin(env: ImportMetaEnv, isBuild: boolean) {
  5 +export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
6 6 const { VITE_GLOB_APP_TITLE, VITE_GLOB_CONTENT_SECURITY_POLICY, VITE_GLOB_PUBLIC_PATH } = env
7 7 const getAppConfigSrc = () => {
8   - const path = VITE_GLOB_PUBLIC_PATH.endsWith('/') ? VITE_GLOB_PUBLIC_PATH : `${VITE_GLOB_PUBLIC_PATH}`
  8 + const path = VITE_GLOB_PUBLIC_PATH?.endsWith('/') ? VITE_GLOB_PUBLIC_PATH : `${VITE_GLOB_PUBLIC_PATH}`
9 9 return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${Date.now()}`
10 10 }
11 11
... ...
  1 +import { Plugin, PluginOption } from "vite";
  2 +import { GenerateBuildConfig } from "./globConfig";
  3 +import { configHtmlPlugin } from "./html";
  4 +
  5 +export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
  6 + const vitePlugins: (Plugin | PluginOption[])[] = []
  7 +
  8 + vitePlugins.push(configHtmlPlugin(viteEnv, isBuild))
  9 + vitePlugins.push(GenerateBuildConfig(viteEnv))
  10 +
  11 + return vitePlugins
  12 +}
... ...
1 1 <!DOCTYPE html>
2 2 <html lang="zh-cmn-Hans">
3   - <head>
4   - <meta charset="UTF-8" />
5   - <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
6   - <meta name="renderer" content="webkit" />
7   - <meta name="description" content="GoView 是高效、高性能的拖拽式低代码数据可视化开发平台,将页面元素封装为基础组件,无需编写代码即可完成业务需求。">
8   - <meta name="keywords" content="GoView,goview,低代码,可视化">
9   - <meta name="author" content="奔跑的面条,面条">
10   - <meta
11   - name="viewport"
12   - content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
13   - />
14   - <link rel="icon" href="./favicon.ico" />
15   - <title>GoView</title>
16   - <link rel="stylesheet" href="./index.css" />
17   - </head>
18   - <body>
19   - <div id="appProvider" style="display: none;"></div>
20   - <div id="app">
21   - <div class="first-loading-wrp">
22   - <div class="loading-wrp">
23   - <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
24   - </div>
  3 +
  4 +<head>
  5 + <%- contentSecurityPolicy %>
  6 + <meta charset="UTF-8" />
  7 + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  8 + <meta name="renderer" content="webkit" />
  9 + <meta name="description" content="GoView 是高效、高性能的拖拽式低代码数据可视化开发平台,将页面元素封装为基础组件,无需编写代码即可完成业务需求。">
  10 + <meta name="keywords" content="GoView,goview,低代码,可视化">
  11 + <meta name="author" content="奔跑的面条,面条">
  12 + <meta name="viewport"
  13 + content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
  14 + <link rel="icon" href="./favicon.ico" />
  15 + <title>
  16 + <%= title %>>
  17 + </title>
  18 + <link rel="stylesheet" href="./index.css" />
  19 +</head>
  20 +
  21 +<body>
  22 + <div id="appProvider" style="display: none;"></div>
  23 + <div id="app">
  24 + <div class="first-loading-wrp">
  25 + <div class="loading-wrp">
  26 + <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
25 27 </div>
26 28 </div>
27   - <script type="module" src="/src/main.ts"></script>
28   - </body>
  29 + </div>
  30 + <script type="module" src="/src/main.ts"></script>
  31 +</body>
  32 +
29 33 </html>
... ...
... ... @@ -6,6 +6,7 @@
6 6 },
7 7 "scripts": {
8 8 "dev": "vite --host",
  9 + "dev:proxy": "vite --host --mode independence",
9 10 "build": "vue-tsc --noEmit && vite build",
10 11 "preview": "vite preview",
11 12 "new": "plop --plopfile ./plop/plopfile.js",
... ...
... ... @@ -51,6 +51,7 @@ export interface UserInfoModel {
51 51 tenantName: string;
52 52 roles: string[];
53 53 plainRoles?: PlainRoleInfo[];
  54 + homePath?: string
54 55 }
55 56
56 57 export interface PlainRoleInfo {
... ...
  1 +export enum RuntimeEnvironment {
  2 + DEVELOPMENT = 'development',
  3 + PRODUCTION = 'production',
  4 + INDEPENDENCE = 'independence'
  5 +}
... ...
... ... @@ -2,8 +2,7 @@ export const PageEnum = {
2 2 SYSTEM_PASSWORD: '/system/changePassword',
3 3 // basic login path
4 4 BASE_LOGIN: '/login',
5   - // basic home path
6   - // BASE_HOME: isDolang.value == '/dashboard/workbench' ? '/dashboard/workbench' : isDolang.value,
  5 + // basic home path
7 6 BASE_HOME: '/dashboard/workbench',
8 7 // error page path
9 8 ERROR_PAGE: '/exception',
... ...
  1 +import { PageEnum } from "@/enums/pageEnum";
  2 +import { Router } from "vue-router"
  3 +
  4 +export const createErrorGuard = (router: Router) => {
  5 + router.beforeEach((to, from, next) => {
  6 + const isErrorPage = router.getRoutes().findIndex((item) => item.name === to.name);
  7 + if (isErrorPage === -1) {
  8 + next({ name: PageEnum.ERROR_PAGE_NAME_404 })
  9 + }
  10 + next()
  11 + })
  12 +
  13 + // 错误
  14 + router.onError((error) => {
  15 + console.log(error, '路由错误');
  16 + });
  17 +}
... ...
  1 +import { Router } from "vue-router"
  2 +import { createErrorGuard } from "./errorGuard"
  3 +import { createLoadingGuard } from "./loadingGuard"
  4 +import { createPermissionGuard } from "./permissionGuard"
  5 +
  6 +export const setupRouterGuard = (router: Router) => {
  7 + createPermissionGuard(router)
  8 + createErrorGuard(router)
  9 + createLoadingGuard(router)
  10 +}
... ...
  1 +import { Router } from "vue-router";
  2 +
  3 +export const createLoadingGuard = (router: Router) => {
  4 + router.beforeEach(async (to, from, next) => {
  5 + const Loading = window['$loading'];
  6 + Loading && Loading.start();
  7 + next()
  8 + })
  9 +
  10 + router.afterEach((to, _, failure) => {
  11 + const Loading = window['$loading'];
  12 + document.title = (to?.meta?.title as string) || document.title;
  13 + Loading && Loading.finish();
  14 + })
  15 +
  16 +}
... ...
  1 +import { RuntimeEnvironment } from "@/enums/external/envEnum";
  2 +import { PageEnum } from "@/enums/pageEnum";
  3 +import { useUserStoreWithOut } from "@/store/external/module/user";
  4 +import { RouteLocationRaw, Router } from "vue-router";
  5 +
  6 +
  7 +const whitePathList: string[] = [PageEnum.BASE_LOGIN];
  8 +
  9 +const isIndependenceMode = import.meta.env.MODE === RuntimeEnvironment.INDEPENDENCE
  10 +
  11 +const toIotPlatformLogin = () => {
  12 + const { origin } = window.location
  13 + window.location.replace(`${origin}/login?redirect=${import.meta.env.VITE_IOT_PLATFORM_PROXY_PREFIX}`)
  14 +}
  15 +
  16 +export function createPermissionGuard(router: Router) {
  17 + const userStore = useUserStoreWithOut()
  18 +
  19 + router.beforeEach((to, from, next) => {
  20 + if (from.path === '/' &&
  21 + to.path === PageEnum.BASE_HOME &&
  22 + userStore.getUserInfo.homePath &&
  23 + userStore.getUserInfo.homePath !== PageEnum.BASE_HOME
  24 + ) {
  25 + console.log(userStore.getUserInfo.homePath)
  26 + next(userStore.getUserInfo.homePath)
  27 + return
  28 + }
  29 +
  30 + const token = userStore.getJwtToken
  31 +
  32 + if (whitePathList.includes(to.path)) {
  33 + if (to.path === PageEnum.BASE_LOGIN && token) {
  34 + const isSessionTimeout = userStore.getSessionTimeout
  35 + try {
  36 + if (!isSessionTimeout) {
  37 + next('/')
  38 + }
  39 + } catch (error) {
  40 + console.error(error)
  41 + }
  42 + }
  43 + else {
  44 + if (isIndependenceMode) {
  45 + toIotPlatformLogin()
  46 + return
  47 + }
  48 + next()
  49 + return
  50 + }
  51 + }
  52 +
  53 + if (!token) {
  54 + const redirectData = {
  55 + path: PageEnum.BASE_LOGIN,
  56 + redirect: true
  57 + } as RouteLocationRaw
  58 + if (isIndependenceMode) {
  59 + toIotPlatformLogin()
  60 + return
  61 + }
  62 + next(redirectData)
  63 + } else {
  64 + next()
  65 + }
  66 +
  67 + })
  68 +}
... ...
... ... @@ -7,6 +7,7 @@ import { HttpErrorPage, LoginRoute, ReloadRoute } from '@/router/base'
7 7 import { Layout } from '@/router/constant'
8 8
9 9 import modules from '@/router/modules'
  10 +import { setupRouterGuard } from './external/guard'
10 11
11 12 const RootRoute: Array<RouteRecordRaw> = [
12 13 {
... ... @@ -39,6 +40,9 @@ const router = createRouter({
39 40 export function setupRouter(app: App) {
40 41 app.use(router);
41 42 // 创建路由守卫
42   - createRouterGuards(router)
  43 + // THINGS_KIT 修改路由拦截
  44 + // createRouterGuards(router)
  45 + setupRouterGuard(router)
  46 +
43 47 }
44 48 export default router
... ...
... ... @@ -53,6 +53,8 @@ const setting: Partial<ProjectConfig> = {
53 53 // Whether to cancel the http request that has been sent but not responded when switching the interface.
54 54 // If it is enabled, I want to overwrite a single interface. Can be set in a separate interface
55 55 removeAllHttpPending: false,
  56 +
  57 + permissionCacheType: CacheTypeEnum.LOCAL
56 58 };
57 59
58 60 export default setting;
... ...
... ... @@ -2,8 +2,7 @@ import type { UserInfo, UserUpdateInfo } from '/#/external/store';
2 2 import type { ErrorMessageMode } from '/#/external/axios';
3 3 import { defineStore } from 'pinia';
4 4 import { pinia as store } from '@/store';
5   -import { RoleEnum } from '@/enums/external/roleEnum';
6   -import { PageEnum } from '@/enums/external/pageEnum';
  5 +import { RoleEnum } from '@/enums/external/roleEnum';
7 6 import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY, ROLES_KEY, USER_INFO_KEY } from '@/enums/external/cacheEnum';
8 7 import { getAuthCache, setAuthCache } from '@/utils/external/auth';
9 8 import {
... ... @@ -17,6 +16,7 @@ import router from '@/router';
17 16 import { createLocalStorage } from '@/utils/external/cache';
18 17 import { useI18n } from 'vue-i18n';
19 18 import { useDialog } from 'naive-ui';
  19 +import { PageEnum } from '@/enums/pageEnum';
20 20
21 21 interface UserState {
22 22 platInfo: any;
... ...
1 1 import type { GlobEnvConfig } from '/#/external/config';
2 2
3   -import pkg from '../../../../package.json';
4   -import { getGlobalConfigName } from '../../../../build/external/globConfig/getGlobConfigName';
  3 +// import pkg from '../../../../package.json';
  4 +import { getGlobalConfigName } from '../../../../build/external/vite/plugins/globConfig/getGlobConfigName';
  5 +import { RuntimeEnvironment } from '@/enums/external/envEnum';
5 6
6 7 export function getCommonStoragePrefix() {
7   - const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
8   - return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase();
  8 + // const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
  9 + const VITE_GLOB_APP_SHORT_NAME = 'undefined'
  10 + const ENV = [RuntimeEnvironment.DEVELOPMENT, RuntimeEnvironment.INDEPENDENCE].includes(import.meta.env.MODE as RuntimeEnvironment) ? RuntimeEnvironment.DEVELOPMENT : RuntimeEnvironment.PRODUCTION
  11 + return `${VITE_GLOB_APP_SHORT_NAME}__${ENV.toUpperCase()}`.toUpperCase();
9 12 }
10 13
11 14 // Generate cache key according to version
12 15 export function getStorageShortName() {
  16 + const pkg = { version: '2.7.1' }
13 17 return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase();
14 18 }
15 19
... ... @@ -20,18 +24,16 @@ export function getAppEnvConfig() {
20 24 ? // Get the global configuration (the configuration will be extracted independently when packaging)
21 25 (import.meta.env as unknown as GlobEnvConfig)
22 26 : window[ENV_NAME as any]) as unknown as GlobEnvConfig;
23   -
  27 +
24 28 const {
25 29 VITE_GLOB_APP_TITLE,
26 30 VITE_GLOB_API_URL,
27 31 VITE_GLOB_APP_SHORT_NAME,
28 32 VITE_GLOB_API_URL_PREFIX,
29 33 VITE_GLOB_UPLOAD_URL,
30   - VITE_GLOB_CONFIGURATION,
31 34 VITE_GLOB_WEB_SOCKET,
32   - VITE_GLOB_CONTENT_SECURITY_POLICY,
33   - VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME,
34 35 VITE_GLOB_ALARM_NOTIFY_DURATION,
  36 + VITE_GLOB_CONTENT_SECURITY_POLICY
35 37 } = ENV;
36 38
37 39 if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
... ... @@ -46,11 +48,9 @@ export function getAppEnvConfig() {
46 48 VITE_GLOB_APP_SHORT_NAME,
47 49 VITE_GLOB_API_URL_PREFIX,
48 50 VITE_GLOB_UPLOAD_URL,
49   - VITE_GLOB_CONFIGURATION,
50 51 VITE_GLOB_WEB_SOCKET,
51   - VITE_GLOB_CONTENT_SECURITY_POLICY,
52   - VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME,
53 52 VITE_GLOB_ALARM_NOTIFY_DURATION,
  53 + VITE_GLOB_CONTENT_SECURITY_POLICY
54 54 };
55 55 }
56 56
... ...
... ... @@ -66,7 +66,7 @@
66 66 </div>
67 67 </n-form-item>
68 68 <n-form-item>
69   - <!-- YUN_TENG loading修改 -->
  69 + <!-- THINGS_KIT loading修改 -->
70 70 <n-button type="primary" @click="handleSubmit" size="large" :loading="loginLoading" block>{{
71 71 $t('login.form_button') }}</n-button>
72 72 </n-form-item>
... ... @@ -173,7 +173,7 @@ const shuffleHandle = () => {
173 173 }, carouselInterval)
174 174 }
175 175
176   -// YUN_TENG 登录钩子
  176 +// THINGS_KIT 登录钩子
177 177 const { login, loading: loginLoading } = useLogin()
178 178
179 179 // 点击事件
... ... @@ -183,19 +183,8 @@ const handleSubmit = (e: Event) => {
183 183 if (!errors) {
184 184 const { username, password } = formInline
185 185 loading.value = true
186   - // YUN_TENG 登录接口
  186 + // THINGS_KIT 登录接口
187 187 await login({ username, password })
188   - // setLocalStorage(
189   - // GO_LOGIN_INFO_STORE,
190   - // cryptoEncode(
191   - // JSON.stringify({
192   - // username,
193   - // password,
194   - // })
195   - // )
196   - // )
197   - // window['$message'].success(`${t('login.login_success')}!`)
198   - // routerTurnByName(PageEnum.BASE_HOME_NAME, true)
199 188
200 189 } else {
201 190 window['$message'].error(`${t('login.login_message')}!`)
... ...
... ... @@ -166,4 +166,6 @@ export interface GlobEnvConfig {
166 166 VITE_GLOB_WEB_SOCKET: string;
167 167 // notify duration
168 168 VITE_GLOB_ALARM_NOTIFY_DURATION: string;
  169 + // content security policy
  170 + VITE_GLOB_CONTENT_SECURITY_POLICY: string
169 171 }
... ...
1 1 /// <reference types="vite/client" />
2 2
3   -
4 3 declare interface GlobEnvConfig {
5 4 // 标题
6 5 VITE_GLOB_APP_TITLE: string;
... ... @@ -26,8 +25,11 @@ declare interface ViteEnv extends GlobEnvConfig {
26 25 VITE_DEV_PATH: string
27 26 // 生产地址
28 27 VITE_PRO_PATH: string
  28 + // iot platform proxy prefix
  29 + VITE_IOT_PLATFORM_PROXY_PREFIX: string
29 30 }
30 31
31 32 interface ImportMetaEnv extends ViteEnv {
  33 + MODE: 'development' | 'production' | 'independence'
32 34 }
33 35
... ...
... ... @@ -6,25 +6,26 @@ import viteCompression from 'vite-plugin-compression'
6 6 import { viteMockServe } from 'vite-plugin-mock'
7 7 import monacoEditorPlugin from 'vite-plugin-monaco-editor'
8 8 import { createProxy } from './build/external/vite/proxy'
9   -import { parseEnv } from './build/external/utils'
10   -import { GenerateBuildConfig } from './build/external/globConfig'
  9 +import { getPublicPath, parseEnv } from './build/external/utils'
  10 +import { createVitePlugins } from './build/external/vite/plugins'
11 11
12 12 function pathResolve(dir: string) {
13 13 return resolve(process.cwd(), '.', dir)
14 14 }
15   -
  15 +// THINGS_KIT 构建方法变更
16 16 export default defineConfig(({ mode, command }) => {
17 17
18 18 const root = process.cwd()
19   -
20   - const env = loadEnv(mode, root)
  19 +
  20 + const env = loadEnv(mode, root)
21 21
22 22 const viteEnv = parseEnv(env)
23 23
24 24 const isBuild = command === 'build'
25 25
26 26 return {
27   - base: '/',
  27 + // THINGS_KIT 修改public path
  28 + base: getPublicPath({ command, mode }, viteEnv),
28 29 // 路径重定向
29 30 resolve: {
30 31 alias: [
... ... @@ -76,7 +77,7 @@ export default defineConfig(({ mode, command }) => {
76 77 algorithm: 'gzip',
77 78 ext: '.gz'
78 79 }),
79   - GenerateBuildConfig(viteEnv)
  80 + ...createVitePlugins(viteEnv, isBuild)
80 81 ],
81 82 build: {
82 83 target: 'es2015',
... ... @@ -88,8 +89,8 @@ export default defineConfig(({ mode, command }) => {
88 89 chunkSizeWarningLimit: chunkSizeWarningLimit
89 90 },
90 91 server: {
91   - proxy: createProxy(viteEnv)
92   - }
  92 + port: 5555,
  93 + proxy: createProxy(viteEnv),
  94 + },
93 95 }
94   -}
95   -)
  96 +})
... ...