fdb2 1.0.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.
Files changed (125) hide show
  1. package/.dockerignore +21 -0
  2. package/.editorconfig +11 -0
  3. package/.eslintrc.cjs +14 -0
  4. package/.eslintrc.json +7 -0
  5. package/.prettierrc.js +3 -0
  6. package/.tpl.env +22 -0
  7. package/README.md +260 -0
  8. package/bin/build.sh +28 -0
  9. package/bin/deploy.sh +8 -0
  10. package/bin/dev.sh +10 -0
  11. package/bin/docker/.env +4 -0
  12. package/bin/docker/dev-docker-compose.yml +43 -0
  13. package/bin/docker/dev.Dockerfile +24 -0
  14. package/bin/docker/prod-docker-compose.yml +17 -0
  15. package/bin/docker/prod.Dockerfile +29 -0
  16. package/bin/fdb2.js +142 -0
  17. package/data/connections.demo.json +32 -0
  18. package/env.d.ts +1 -0
  19. package/nw-build.js +120 -0
  20. package/nw-dev.js +65 -0
  21. package/package.json +114 -0
  22. package/public/favicon.ico +0 -0
  23. package/public/index.html +9 -0
  24. package/public/modules/header.tpl +14 -0
  25. package/public/modules/initial_state.tpl +55 -0
  26. package/server/index.ts +677 -0
  27. package/server/model/connection.entity.ts +66 -0
  28. package/server/model/database.entity.ts +246 -0
  29. package/server/service/connection.service.ts +334 -0
  30. package/server/service/database/base.service.ts +363 -0
  31. package/server/service/database/database.service.ts +510 -0
  32. package/server/service/database/index.ts +7 -0
  33. package/server/service/database/mssql.service.ts +723 -0
  34. package/server/service/database/mysql.service.ts +761 -0
  35. package/server/service/database/oracle.service.ts +839 -0
  36. package/server/service/database/postgres.service.ts +744 -0
  37. package/server/service/database/sqlite.service.ts +559 -0
  38. package/server/service/session.service.ts +158 -0
  39. package/server.js +128 -0
  40. package/src/adapter/ajax.ts +135 -0
  41. package/src/assets/base.css +1 -0
  42. package/src/assets/database.css +950 -0
  43. package/src/assets/images/collapse.png +0 -0
  44. package/src/assets/images/no-login.png +0 -0
  45. package/src/assets/images/svg/illustrations/illustration-1.svg +1 -0
  46. package/src/assets/images/svg/illustrations/illustration-2.svg +2 -0
  47. package/src/assets/images/svg/illustrations/illustration-3.svg +50 -0
  48. package/src/assets/images/svg/illustrations/illustration-4.svg +1 -0
  49. package/src/assets/images/svg/illustrations/illustration-5.svg +73 -0
  50. package/src/assets/images/svg/illustrations/illustration-6.svg +89 -0
  51. package/src/assets/images/svg/illustrations/illustration-7.svg +39 -0
  52. package/src/assets/images/svg/illustrations/illustration-8.svg +1 -0
  53. package/src/assets/images/svg/separators/curve-2.svg +3 -0
  54. package/src/assets/images/svg/separators/curve.svg +3 -0
  55. package/src/assets/images/svg/separators/line.svg +3 -0
  56. package/src/assets/images/theme/light/screen-1-1000x800.jpg +0 -0
  57. package/src/assets/images/theme/light/screen-2-1000x800.jpg +0 -0
  58. package/src/assets/login/bg.jpg +0 -0
  59. package/src/assets/login/bg.png +0 -0
  60. package/src/assets/login/left.jpg +0 -0
  61. package/src/assets/logo.svg +73 -0
  62. package/src/assets/logo.webp +0 -0
  63. package/src/assets/main.css +1 -0
  64. package/src/base/config.ts +20 -0
  65. package/src/base/detect.ts +134 -0
  66. package/src/base/entity.ts +92 -0
  67. package/src/base/eventBus.ts +37 -0
  68. package/src/base//345/237/272/347/241/200/345/261/202.md +7 -0
  69. package/src/components/connection-editor/index.vue +590 -0
  70. package/src/components/dataGrid/index.vue +105 -0
  71. package/src/components/dataGrid/pagination.vue +106 -0
  72. package/src/components/loading/index.vue +43 -0
  73. package/src/components/modal/index.ts +181 -0
  74. package/src/components/modal/index.vue +560 -0
  75. package/src/components/toast/index.ts +44 -0
  76. package/src/components/toast/toast.vue +58 -0
  77. package/src/components/user/name.vue +104 -0
  78. package/src/components/user/selector.vue +416 -0
  79. package/src/domain/SysConfig.ts +74 -0
  80. package/src/platform/App.vue +8 -0
  81. package/src/platform/database/components/connection-detail.vue +1154 -0
  82. package/src/platform/database/components/data-editor.vue +478 -0
  83. package/src/platform/database/components/data-import-export.vue +1602 -0
  84. package/src/platform/database/components/database-detail.vue +1173 -0
  85. package/src/platform/database/components/database-monitor.vue +1086 -0
  86. package/src/platform/database/components/db-tools.vue +577 -0
  87. package/src/platform/database/components/query-history.vue +1349 -0
  88. package/src/platform/database/components/sql-executor.vue +738 -0
  89. package/src/platform/database/components/sql-query-editor.vue +1046 -0
  90. package/src/platform/database/components/table-detail.vue +1376 -0
  91. package/src/platform/database/components/table-editor.vue +690 -0
  92. package/src/platform/database/explorer.vue +1840 -0
  93. package/src/platform/database/index.vue +1193 -0
  94. package/src/platform/database/layout.vue +367 -0
  95. package/src/platform/database/router.ts +37 -0
  96. package/src/platform/database/styles/common.scss +602 -0
  97. package/src/platform/database/types/common.ts +445 -0
  98. package/src/platform/database/utils/export.ts +232 -0
  99. package/src/platform/database/utils/helpers.ts +437 -0
  100. package/src/platform/index.ts +33 -0
  101. package/src/platform/router.ts +41 -0
  102. package/src/service/base.ts +128 -0
  103. package/src/service/database.ts +500 -0
  104. package/src/service/login.ts +121 -0
  105. package/src/shims-vue.d.ts +7 -0
  106. package/src/stores/connection.ts +266 -0
  107. package/src/stores/session.ts +87 -0
  108. package/src/typings/database-types.ts +413 -0
  109. package/src/typings/database.ts +364 -0
  110. package/src/typings/global.d.ts +58 -0
  111. package/src/typings/pinia.d.ts +8 -0
  112. package/src/utils/clipboard.ts +30 -0
  113. package/src/utils/database-types.ts +243 -0
  114. package/src/utils/modal.ts +124 -0
  115. package/src/utils/request.ts +55 -0
  116. package/src/utils/sleep.ts +4 -0
  117. package/src/utils/toast.ts +73 -0
  118. package/src/utils/util.ts +171 -0
  119. package/src/utils/xlsx.ts +228 -0
  120. package/tsconfig.json +33 -0
  121. package/tsconfig.server.json +19 -0
  122. package/view/index.html +9 -0
  123. package/view/modules/header.tpl +14 -0
  124. package/view/modules/initial_state.tpl +20 -0
  125. package/vite.config.ts +384 -0
@@ -0,0 +1,158 @@
1
+ import { Provide, Scope, ScopeEnum, Config, Inject } from '@midwayjs/core';
2
+ import { ISessionService, TencentService } from '@cicctencent/midwayjs-base';
3
+ import { Session, GetLoginSessionReq, GetLoginSessionRes, LogoutReq, LogoutRes, LoginByWxReq, LoginByWxRsp, LoginByAccountReq, LoginByAccountRsp } from '@fefeding/common/dist/models/account/session';
4
+
5
+ @Provide('session:service')
6
+ @Scope(ScopeEnum.Request, { allowDowngrade: true })
7
+ export class SessionService extends ISessionService {
8
+ @Config('loginOption')
9
+ loginOption: any;
10
+
11
+ @Inject()
12
+ tencentService: TencentService;
13
+
14
+ /**
15
+ * 根据ID获取session
16
+ * 并判断session是否在有效期
17
+ * @param id token唯一
18
+ */
19
+ async getLoginSession(id: string): Promise<Session> {
20
+ const req = new GetLoginSessionReq();
21
+ req.id = id;
22
+ const res = await this.requestBaseApi<GetLoginSessionRes>(req);
23
+ return res?.data || null;
24
+ }
25
+ /**
26
+ * 下线
27
+ * @param id 需要下线的id或者session
28
+ */
29
+ async logout(id: string): Promise<any> {
30
+ const req = new LogoutReq();
31
+ req.id = id;
32
+ const res = await this.requestBaseApi<LogoutRes>(req);
33
+ this.ctx.currentSession = null;
34
+ return res;
35
+ }
36
+
37
+ /**
38
+ * 登录接口
39
+ * @param loginParams 登录参数
40
+ * @returns
41
+ */
42
+ async loginByWx(loginParams: LoginByWxReq) {
43
+ const req = new LoginByWxReq();
44
+ req.fromJSON(loginParams);
45
+ const res = await this.requestBaseApi<LoginByWxRsp>(req);
46
+ return res;
47
+ }
48
+
49
+ /**
50
+ * 帐号登陆
51
+ * @param loginParams
52
+ * @returns
53
+ */
54
+ async loginByAccount(loginParams: LoginByAccountReq) {
55
+ loginParams.appId = '0';//this.baseServiceOption.appId?.toString() || '0';
56
+
57
+ const req = new LoginByAccountReq();
58
+ req.fromJSON(loginParams);
59
+ const res = await this.requestBaseApi<LoginByAccountRsp>(req);
60
+ return res;
61
+ }
62
+
63
+ /**
64
+ * 用临时码登陆
65
+ * @param code 登陆临时码
66
+ * @returns
67
+ */
68
+ async loginByCode(code: string) {
69
+ const res = await this.requestBaseApi('/api/session/loginByAuthCode', {
70
+ data: {
71
+ code
72
+ }
73
+ });
74
+ return res?.data || null;
75
+ }
76
+
77
+ /**
78
+ * 生成临时登陆码
79
+ * @param id 登陆token
80
+ * @returns
81
+ */
82
+ async createAuthCode(id: string) {
83
+ const res = await this.requestBaseApi('/api/session/createAuthCode', {
84
+ data: {
85
+ id
86
+ }
87
+ });
88
+ return res;
89
+ }
90
+
91
+ // 生成登陆用公众号二维码
92
+ // 先生成一个验证码,当用户扫码后,事件回调获取验证码后再生成session
93
+ async createWxLoginQrcode() {
94
+
95
+ const res = await this.requestBaseApi('/api/session/createWxLoginQrcode', {
96
+ data: {
97
+ appId: this.loginOption.wxAppId
98
+ }
99
+ });
100
+ //console.log(res);
101
+ return res;
102
+ }
103
+
104
+ // 验证码登陆
105
+ async loginByVerifyCode(code: string, appId?: number,prefix?: string) {
106
+ if(!code) throw Error('code不可为空');
107
+ appId = appId || this.loginOption.wxAppId;
108
+
109
+ const res = await this.requestBaseApi('/api/session/loginByVerifyCode', {
110
+ data: {
111
+ appId,
112
+ code,
113
+ prefix
114
+ }
115
+ });
116
+ return res;
117
+ }
118
+
119
+ // 更新用户信息
120
+ async updateUser(user: any) {
121
+ const userId = this.ctx.currentSession?.userId || 0;
122
+ if(!userId) return;
123
+ //console.log(loginParams, req);
124
+ const res = await this.requestBaseApi('/api/account/updateUser', {
125
+ data: {
126
+ user: {
127
+ ...user,
128
+ id: userId,
129
+ }
130
+ },
131
+ params: {
132
+ token: this.ctx.currentSession?.id
133
+ }
134
+ });
135
+ console.log('updateUser', res);
136
+ return res;
137
+ }
138
+
139
+ // 上传头像图片
140
+ async uploadAvatar(file: any) {
141
+
142
+ const userId = this.ctx.currentSession?.userId || 0;
143
+ if(!userId) return;
144
+
145
+ let filename = file['filename'] as string || '1.png';
146
+
147
+ const key = `avator/${userId}/${filename}`;
148
+
149
+ const res = await this.tencentService.uploadCosFile(key, file.data);
150
+ console.log(res);
151
+ const user = {
152
+ id: userId,
153
+ avatar: res.url || key,
154
+ }
155
+
156
+ return await this.updateUser(user);
157
+ }
158
+ }
package/server.js ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+
3
+ const express = require('express');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ // 日志文件路径 - 使用绝对路径
8
+ const logFilePath = path.resolve(__dirname, 'server.log');
9
+
10
+ // 重定向控制台输出到日志文件
11
+ const logStream = fs.createWriteStream(logFilePath, { flags: 'a' });
12
+
13
+ // 保存原始的 console.log 和 console.error
14
+ const originalLog = console.log;
15
+ const originalError = console.error;
16
+
17
+ // 重写 console.log
18
+ console.log = function(...args) {
19
+ const output = args.join(' ') + '\n';
20
+ logStream.write(output);
21
+ originalLog.apply(console, args);
22
+ };
23
+
24
+ // 重写 console.error
25
+ console.error = function(...args) {
26
+ const output = args.join(' ') + '\n';
27
+ logStream.write(output);
28
+ originalError.apply(console, args);
29
+ };
30
+
31
+ // 创建 express 应用
32
+ const app = express();
33
+
34
+ // 静态文件目录
35
+ const staticDir = path.join(__dirname, 'dist');
36
+
37
+ // 解析 JSON 请求体
38
+ app.use(express.json());
39
+
40
+ // 设置 CORS 头
41
+ app.use((req, res, next) => {
42
+ res.setHeader('Access-Control-Allow-Origin', '*');
43
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
44
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
45
+ next();
46
+ });
47
+
48
+ // 处理 API 请求
49
+ app.use('/api/', async (req, res, next) => {
50
+ if (req.method === 'POST') {
51
+ try {
52
+ const serverModule = require('./dist/server/index.js');
53
+ const handleDatabaseRoutes = serverModule.handleDatabaseRoutes;
54
+
55
+ // 调用 handleDatabaseRoutes 函数处理请求
56
+ const result = await handleDatabaseRoutes(req.originalUrl, req.body);
57
+
58
+ // 返回处理结果
59
+ res.status(200).json({
60
+ ret: 0,
61
+ msg: 'success',
62
+ data: result
63
+ });
64
+ } catch (error) {
65
+ console.error('API Error:', error);
66
+ res.status(500).json({
67
+ ret: 500,
68
+ msg: error.message || 'Internal server error'
69
+ });
70
+ }
71
+ } else {
72
+ next();
73
+ }
74
+ });
75
+
76
+ // 配置静态文件目录 - 只有 /public 请求指向 public 目录
77
+ app.use('/public', express.static(path.join(staticDir, 'public')));
78
+
79
+ // 所有未匹配的路由都指向 index.html
80
+ app.use((req, res) => {
81
+ const indexPath = path.join(staticDir, 'view', 'index.html');
82
+ res.sendFile(indexPath, (err) => {
83
+ if (err) {
84
+ res.status(500).send('Error loading index.html');
85
+ }
86
+ });
87
+ });
88
+
89
+ // 解析命令行参数获取端口
90
+ let portFromArgs;
91
+ for (let i = 0; i < process.argv.length; i++) {
92
+ if ((process.argv[i] === '--port' || process.argv[i] === '-p') && process.argv[i + 1]) {
93
+ portFromArgs = parseInt(process.argv[i + 1]);
94
+ break;
95
+ }
96
+ }
97
+
98
+ // 启动服务器
99
+ const PORT = portFromArgs || process.env.PORT || 9300;
100
+ app.listen(PORT, () => {
101
+
102
+ // 将 PID 写入 PID 文件
103
+ const pidFilePath = path.join(__dirname, 'server.pid');
104
+ fs.writeFileSync(pidFilePath, process.pid.toString());
105
+ console.log(`PID ${process.pid} written to ${pidFilePath}`);
106
+
107
+ console.log(`Server is running on port ${PORT}`);
108
+ console.log(`http://localhost:${PORT}`);
109
+ });
110
+
111
+ // 处理进程退出事件,关闭日志流
112
+ process.on('exit', () => {
113
+ logStream.end();
114
+ });
115
+
116
+ // 处理未捕获的异常
117
+ process.on('uncaughtException', (err) => {
118
+ console.error('Uncaught exception:', err);
119
+ logStream.end();
120
+ process.exit(1);
121
+ });
122
+
123
+ // 处理未处理的 Promise 拒绝
124
+ process.on('unhandledRejection', (err) => {
125
+ console.error('Unhandled rejection:', err);
126
+ logStream.end();
127
+ process.exit(1);
128
+ });
@@ -0,0 +1,135 @@
1
+ import type { AxiosRequestConfig, AxiosResponse } from 'axios';
2
+ import axios from 'axios';
3
+ import baseConf from '@/base/config';
4
+ export const pendingMap = new Map();
5
+ export const service = axios.create({
6
+ baseURL: `${baseConf.baseURL}`, // 设置统一的请求前缀// http://localhost:8888
7
+ timeout: 30000, // 设置统一的超时时长
8
+ });
9
+ service.interceptors.request.use(
10
+ config => {
11
+ const pendingKey = getPendingKey(config);
12
+ if (pendingMap.has(pendingKey)) {
13
+ const controller = pendingMap.get(pendingKey);
14
+ controller.abort();
15
+ } else {
16
+ addPending(config);
17
+ }
18
+
19
+ return config;
20
+ },
21
+ error => {
22
+ return Promise.reject(error);
23
+ }
24
+ );
25
+ service.interceptors.response.use(
26
+ response => {
27
+ removePending(response.config);
28
+ return response;
29
+ },
30
+ error => {
31
+ error.config && removePending(error.config);
32
+ error.message = getHttpErrorMsg(error);
33
+ return Promise.reject(error);
34
+ }
35
+ );
36
+ type Config = AxiosRequestConfig & { debounceRequest?: boolean };
37
+ export function request(axiosConfig: Config): Promise<AxiosResponse<any, any>> {
38
+ // 自定义配置
39
+ const options = Object.assign(
40
+ {
41
+ debounceRequest: true, // 是否开启取消重复请求, 默认为 true
42
+ },
43
+ axiosConfig
44
+ );
45
+
46
+ return service(options);
47
+ }
48
+
49
+ /**
50
+ * 生成每个请求唯一的键
51
+ * @param {*} config
52
+ * @returns string
53
+ */
54
+ function getPendingKey(config: AxiosRequestConfig) {
55
+ const { url, method, params } = config;
56
+ let { data } = config;
57
+ if (typeof data === 'string') {
58
+ data = JSON.parse(data);
59
+ }
60
+ // 以url和...组成字符串作为储存的key值
61
+ return [url, method, JSON.stringify(params), JSON.stringify(data)].join(
62
+ '&'
63
+ );
64
+ }
65
+ /**
66
+ * 储存每个请求唯一值, 也就是cancel()方法, 用于取消请求
67
+ *
68
+ * @param {*} config
69
+ */
70
+ function addPending(config: AxiosRequestConfig) {
71
+ const pendingKey = getPendingKey(config);
72
+ const controller = new AbortController();
73
+ config.signal = controller.signal;
74
+ pendingMap.set(pendingKey, controller);
75
+ }
76
+ /**
77
+ * 删除重复的请求key
78
+ * @param {*} config
79
+ */
80
+ function removePending(config: AxiosRequestConfig) {
81
+ const pendingKey = getPendingKey(config);
82
+ if (pendingMap.has(pendingKey)) {
83
+ pendingMap.delete(pendingKey);
84
+ }
85
+ }
86
+ /**
87
+ * 取消所有请求
88
+ * @param {*} config
89
+ */
90
+ export function abortAllPending() {
91
+ for (const [key, value] of pendingMap.entries()) {
92
+ value.abort();
93
+ pendingMap.delete(key);
94
+ }
95
+ }
96
+ /**
97
+ * 处理异常
98
+ * @param {*} error
99
+ */
100
+ function getHttpErrorMsg(error: any) {
101
+ let message = '';
102
+ // 处理被取消的请求
103
+ if (axios.isCancel(error)) {
104
+ console.error(
105
+ `重复请求:${(error as any).config.url} ${error.message}`
106
+ );
107
+ }
108
+
109
+ if (error && error.response) {
110
+ switch (error.response.status) {
111
+ case 302:
112
+ message = '接口重定向了!';
113
+ break;
114
+ case 400:
115
+ message = '参数不正确!';
116
+ break;
117
+ case 401:
118
+ message = '您未登录,或者登录已经超时,请先登录!';
119
+ break;
120
+ case 403:
121
+ message = '您没有权限操作!';
122
+ break;
123
+ case 504:
124
+ message = '服务暂时无法访问,请稍后再试!';
125
+ break;
126
+ default:
127
+ message = '异常问题,请联系管理员!';
128
+ break;
129
+ }
130
+ }
131
+ if (error.message.includes('timeout')) message = '网络请求超时!';
132
+ if (error.message.includes('Network'))
133
+ message = window.navigator.onLine ? '服务端异常!' : '您断网了!';
134
+ return message;
135
+ }
@@ -0,0 +1 @@
1
+ /* color palette from <https://github.com/vuejs/theme> */