chattransfer 0.0.0-stage → 2.0.10

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.ar.md +208 -0
  3. package/README.md +207 -2
  4. package/README.zh-CN.md +208 -0
  5. package/bin/chattransfer.js +38 -0
  6. package/frontend/README.md +5 -0
  7. package/frontend/dist/assets/browser-BqOeEQBb.js +8 -0
  8. package/frontend/dist/assets/index-DTsuxum5.js +976 -0
  9. package/frontend/dist/assets/index-Dw5X-Gok.css +1 -0
  10. package/frontend/dist/assets/logo-DQUxWwh1.png +0 -0
  11. package/frontend/dist/index.html +14 -0
  12. package/frontend/dist/pin.svg +1 -0
  13. package/frontend/dist/vite.svg +1 -0
  14. package/package.json +62 -4
  15. package/server/cli-options.js +69 -0
  16. package/server/config/loader.js +36 -0
  17. package/server/config.js +63 -0
  18. package/server/index.js +132 -0
  19. package/server/room-manager.js +75 -0
  20. package/server/routes/checkLogin.js +24 -0
  21. package/server/routes/files.js +49 -0
  22. package/server/routes/geneBUserQR.js +37 -0
  23. package/server/routes/geneQR.js +39 -0
  24. package/server/routes/getHomeDir.js +27 -0
  25. package/server/routes/getIndexInfo.js +42 -0
  26. package/server/routes/getIndexInfo2.js +42 -0
  27. package/server/routes/index.js +36 -0
  28. package/server/routes/openCachePath.js +63 -0
  29. package/server/routes/qr.js +63 -0
  30. package/server/routes/scanLogin.js +33 -0
  31. package/server/routes/session.js +15 -0
  32. package/server/routes/setCachePath.js +94 -0
  33. package/server/routes/system.js +107 -0
  34. package/server/services/message-history-service.js +476 -0
  35. package/server/services/message-service.js +31 -0
  36. package/server/socket/index.js +232 -0
  37. package/server/socket/session-manager.js +3 -0
  38. package/server/telemetry/amplitude-provider.js +17 -0
  39. package/server/telemetry/compat.js +17 -0
  40. package/server/telemetry/index.js +26 -0
  41. package/server/telemetry/noop-provider.js +7 -0
  42. package/server/tool.js +71 -0
  43. package/server/utils/app-root.js +11 -0
  44. package/server/utils/network.js +21 -0
  45. package/server/utils/request-url.js +15 -0
  46. package/server/utils/runtime-options.js +88 -0
  47. package/server/utils/runtime-paths.js +87 -0
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" href="/assets/logo-DQUxWwh1.png" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>ChatTransfer</title>
8
+ <script type="module" crossorigin src="/assets/index-DTsuxum5.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-Dw5X-Gok.css">
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1776763670183" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2129" id="mx_n_1776763670184" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M245.430857 657.718857h228.845714v220.288c0 65.133714 27.428571 119.990857 38.144 119.990857 10.294857 0 37.723429-54.857143 37.723429-119.990857V657.718857h228.425143c29.568 0 51.017143-18.870857 51.017143-47.579428 0-17.993143-5.595429-30.848-18.011429-44.141715l-131.145143-142.281143c-8.996571-9.435429-14.573714-16.274286-12.434285-33.865142l21.869714-156.416c1.28-8.996571 2.139429-14.153143 10.276571-19.730286l104.996572-76.269714c23.588571-17.152 33.865143-38.144 33.865143-57.435429 0-28.708571-23.149714-53.997714-56.137143-53.997714H241.115429c-32.987429 0-56.137143 25.289143-56.137143 53.997714 0 19.291429 10.294857 40.283429 33.426285 57.417143l105.435429 76.306286c8.137143 5.558857 8.996571 10.697143 10.276571 19.712l21.869715 156.416c2.56 17.572571-3.437714 24.429714-12.434286 33.865142l-131.145143 142.281143c-12.434286 13.293714-17.993143 26.148571-17.993143 44.141715 0 28.708571 21.430857 47.579429 50.998857 47.579428z m55.716572-69.010286c-3.858286 0-6.436571-2.56-6.436572-6.418285 0-2.56 1.718857-5.156571 3.858286-7.296l109.714286-114.834286c14.153143-14.153143 19.712-29.586286 18.011428-45.44l-26.587428-206.573714c-2.139429-16.713143-7.716571-29.147429-20.132572-37.723429l-93.001143-61.714286c-2.998857-2.139429-3.858286-4.278857-3.858285-7.277714 0-3.858286 2.998857-6.436571 6.857142-6.436571h444.854858c3.858286 0 6.857143 2.56 6.857142 6.436571 0 2.998857-0.859429 5.138286-3.84 7.277714L644.406857 170.422857c-12.434286 8.594286-17.993143 21.010286-20.132571 37.723429L597.723429 414.72c-1.718857 15.853714 3.84 31.268571 17.554285 45.421714l110.153143 114.852572a10.569143 10.569143 0 0 1 3.419429 7.296c0 3.84-2.139429 6.418286-5.997715 6.418285z" p-id="2130" fill="#1DA993"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "chattransfer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.10",
4
+ "description": "LAN instant messaging and file transfer tool",
5
+ "main": "server/index.js",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "scripts": {
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "dev": "node server/index.js",
13
+ "build:frontend": "npm --prefix frontend ci && npm --prefix frontend run build",
14
+ "prepublishOnly": "npm test && npm run build:frontend && npm pack --dry-run",
15
+ "release:check": "sh -c 'set -e; VERSION=$(node -p \"require(\\\"./package.json\\\").version\"); TAG=v$VERSION; if git rev-parse -q --verify \"$TAG\" >/dev/null; then echo \"Tag $TAG already exists\"; exit 1; fi; echo \"Version $VERSION is ready for tag $TAG\"'"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/turboxing/ChatTransfer.git"
20
+ },
21
+ "author": "suncx",
22
+ "license": "MIT",
23
+ "bugs": {
24
+ "url": "https://github.com/turboxing/ChatTransfer/issues"
25
+ },
26
+ "homepage": "https://github.com/turboxing/ChatTransfer",
27
+ "dependencies": {
28
+ "axios": "^1.13.5",
29
+ "express": "^4.17.1",
30
+ "express-fileupload": "^1.4.0",
31
+ "moment": "^2.27.0",
32
+ "node-machine-id": "^1.1.12",
33
+ "qr-image": "^3.2.0",
34
+ "sqlite3": "5.1.7",
35
+ "socket.io": "^4.8.3",
36
+ "underscore": "^1.10.2",
37
+ "urlencode": "^1.1.0"
38
+ },
39
+ "bin": {
40
+ "chattransfer": "bin/chattransfer.js"
41
+ },
42
+ "files": [
43
+ "bin",
44
+ "server",
45
+ "frontend/dist/**/*",
46
+ "README.md",
47
+ "LICENSE"
48
+ ],
49
+ "pkg": {
50
+ "assets": [
51
+ "frontend/dist/**/*",
52
+ "node_modules/sqlite3/build/Release/node_sqlite3.node"
53
+ ],
54
+ "scripts": [
55
+ "server/**/*.js"
56
+ ],
57
+ "outputPath": "dist"
58
+ },
59
+ "devDependencies": {
60
+ "pkg": "^5.8.1",
61
+ "supertest": "^7.2.2",
62
+ "vitest": "^1.6.1"
63
+ }
64
+ }
@@ -0,0 +1,69 @@
1
+ const { parseArgs } = require('node:util');
2
+
3
+ const CLI_OPTIONS = {
4
+ host: { type: 'string' },
5
+ port: { type: 'string' },
6
+ 'data-dir': { type: 'string' },
7
+ 'public-url': { type: 'string' },
8
+ 'no-browser': { type: 'boolean', default: false },
9
+ version: { type: 'boolean', default: false },
10
+ help: { type: 'boolean', default: false }
11
+ };
12
+
13
+ function parseCliArguments(argv = process.argv.slice(2)) {
14
+ try {
15
+ const { values } = parseArgs({
16
+ args: argv,
17
+ options: CLI_OPTIONS,
18
+ strict: true,
19
+ allowPositionals: false
20
+ });
21
+
22
+ return values;
23
+ } catch (error) {
24
+ throw new Error(error.message);
25
+ }
26
+ }
27
+
28
+ function toStartOptions(cliValues, env = process.env) {
29
+ const { resolveStartOptions } = require('./utils/runtime-options');
30
+
31
+ return resolveStartOptions({
32
+ host: cliValues.host,
33
+ port: cliValues.port,
34
+ dataDir: cliValues['data-dir'],
35
+ publicUrl: cliValues['public-url'],
36
+ openBrowser: cliValues['no-browser'] ? false : undefined
37
+ }, env);
38
+ }
39
+
40
+ function getHelpText() {
41
+ return [
42
+ 'ChatTransfer - LAN instant messaging and file transfer',
43
+ '',
44
+ 'Usage:',
45
+ ' chattransfer [options]',
46
+ '',
47
+ 'Options:',
48
+ ' --host <host> Listen host (default: 0.0.0.0)',
49
+ ' --port <port> Listen port (default: 55555)',
50
+ ' --data-dir <path> Runtime data directory',
51
+ ' --public-url <url> Public URL used by QR codes and links',
52
+ ' --no-browser Do not open the local browser',
53
+ ' --version Print the version',
54
+ ' --help Print this help',
55
+ '',
56
+ 'Environment variables:',
57
+ ' CHATTRANSFER_HOST',
58
+ ' CHATTRANSFER_PORT',
59
+ ' CHATTRANSFER_DATA_DIR',
60
+ ' CHATTRANSFER_PUBLIC_URL',
61
+ ' CHATTRANSFER_NO_BROWSER'
62
+ ].join('\n');
63
+ }
64
+
65
+ module.exports = {
66
+ getHelpText,
67
+ parseCliArguments,
68
+ toStartOptions
69
+ };
@@ -0,0 +1,36 @@
1
+ const fs = require('fs');
2
+ const { getTelemetryConfigFile } = require('../utils/runtime-paths');
3
+
4
+ const DEFAULT_CONFIG = {
5
+ analytics: {
6
+ enabled: false,
7
+ apiKey: ''
8
+ }
9
+ };
10
+
11
+ function loadConfig() {
12
+ const configPath = getTelemetryConfigFile();
13
+
14
+ try {
15
+ if (!fs.existsSync(configPath)) {
16
+ return { ...DEFAULT_CONFIG };
17
+ }
18
+
19
+ const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
20
+ return {
21
+ ...DEFAULT_CONFIG,
22
+ ...parsed,
23
+ analytics: {
24
+ ...DEFAULT_CONFIG.analytics,
25
+ ...(parsed.analytics || {})
26
+ }
27
+ };
28
+ } catch (error) {
29
+ console.error('[Config] load failed:', error.message);
30
+ return { ...DEFAULT_CONFIG };
31
+ }
32
+ }
33
+
34
+ module.exports = {
35
+ loadConfig
36
+ };
@@ -0,0 +1,63 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const {
4
+ getConfigFile,
5
+ getDefaultDataDir
6
+ } = require('./utils/runtime-paths');
7
+
8
+ const DEFAULT_CONFIG = { cachePath: null };
9
+
10
+ function loadConfig(configFile = getConfigFile()) {
11
+ try {
12
+ if (fs.existsSync(configFile)) {
13
+ const raw = fs.readFileSync(configFile, 'utf8');
14
+ const parsed = raw ? JSON.parse(raw) : {};
15
+ return { ...DEFAULT_CONFIG, ...parsed };
16
+ }
17
+ } catch (e) {
18
+ console.error('[Config] load failed:', e.message);
19
+ }
20
+ return { ...DEFAULT_CONFIG };
21
+ }
22
+
23
+ function saveConfig(cfg, configFile = getConfigFile()) {
24
+ const tmp = `${configFile}.tmp`;
25
+
26
+ fs.mkdirSync(path.dirname(configFile), { recursive: true });
27
+ fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2), 'utf8');
28
+ fs.renameSync(tmp, configFile);
29
+ }
30
+
31
+ function getDefaultCachePath() {
32
+ return path.join(getDefaultDataDir(), 'uploads');
33
+ }
34
+
35
+ function getCachePath() {
36
+ const cfg = loadConfig();
37
+ if (cfg.cachePath && fs.existsSync(cfg.cachePath)) {
38
+ return cfg.cachePath;
39
+ }
40
+ return getDefaultCachePath();
41
+ }
42
+
43
+ function isCustomized() {
44
+ const cfg = loadConfig();
45
+ return !!(cfg.cachePath && cfg.cachePath !== getDefaultCachePath());
46
+ }
47
+
48
+ function setCachePath(p) {
49
+ const cfg = loadConfig();
50
+ cfg.cachePath = p;
51
+ saveConfig(cfg);
52
+ }
53
+
54
+ module.exports = {
55
+ getConfigFile,
56
+ loadConfig,
57
+ saveConfig,
58
+ getCachePath,
59
+ setCachePath,
60
+ getDefaultCachePath,
61
+ isCustomized,
62
+ DEFAULT_CONFIG
63
+ };
@@ -0,0 +1,132 @@
1
+ const http = require('http');
2
+ const net = require('net');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { getIPAddress, openDefaultBrowser } = require('./tool');
6
+ const { getCachePath, getConfigFile } = require('./config');
7
+ const registerRoutes = require('./routes/index');
8
+ const registerSocket = require('./socket/index');
9
+ const { MessageHistoryService } = require('./services/message-history-service');
10
+ const { trackEvent } = require('./telemetry/compat');
11
+ const {
12
+ getDatabaseFile,
13
+ getRuntimeName,
14
+ usesIndependentDatabaseFile
15
+ } = require('./utils/runtime-paths');
16
+ const { resolveStartOptions } = require('./utils/runtime-options');
17
+
18
+ const DEFAULT_PORT = 55555;
19
+ const MAX_RETRY = 3;
20
+ const clientIpAddress = getIPAddress() || '127.0.0.1';
21
+ const pkg = require('../package.json');
22
+
23
+ function createApp() {
24
+ const app = require('express')();
25
+ registerRoutes(app);
26
+ return app;
27
+ }
28
+
29
+ function canListen(port, host) {
30
+ return new Promise((resolve) => {
31
+ const tester = net.createServer();
32
+
33
+ tester.once('error', () => resolve(false));
34
+ tester.once('listening', () => tester.close(() => resolve(true)));
35
+ tester.listen(port, host);
36
+ });
37
+ }
38
+
39
+ async function resolvePort({ host, port, explicitPort }) {
40
+ if (explicitPort) {
41
+ return await canListen(port, host) ? port : null;
42
+ }
43
+
44
+ for (let index = 0; index <= MAX_RETRY; index += 1) {
45
+ const candidatePort = DEFAULT_PORT + index;
46
+
47
+ if (await canListen(candidatePort, host)) {
48
+ return candidatePort;
49
+ }
50
+ }
51
+
52
+ return null;
53
+ }
54
+
55
+ async function start(options = {}) {
56
+ const runtimeOptions = resolveStartOptions(options);
57
+
58
+ if (runtimeOptions.dataDir) {
59
+ process.env.CHATTRANSFER_DATA_DIR = runtimeOptions.dataDir;
60
+ }
61
+
62
+ if (runtimeOptions.publicUrl) {
63
+ process.env.CHATTRANSFER_PUBLIC_URL = runtimeOptions.publicUrl;
64
+ }
65
+
66
+ const port = await resolvePort(runtimeOptions);
67
+
68
+ if (!port) {
69
+ console.error(runtimeOptions.explicitPort
70
+ ? `Port ${runtimeOptions.port} is in use`
71
+ : `Ports ${DEFAULT_PORT}-${DEFAULT_PORT + MAX_RETRY} are in use`);
72
+ return;
73
+ }
74
+
75
+ const uploadsDir = getCachePath();
76
+ await fs.promises.mkdir(uploadsDir, { recursive: true });
77
+
78
+ const historyService = new MessageHistoryService({
79
+ cachePath: uploadsDir,
80
+ databasePath: usesIndependentDatabaseFile() ? getDatabaseFile() : undefined
81
+ });
82
+
83
+ try {
84
+ await historyService.init();
85
+ } catch (error) {
86
+ console.error('[History] initialization failed:', error.message);
87
+ return;
88
+ }
89
+
90
+ const app = createApp();
91
+ const server = http.createServer(app);
92
+
93
+ registerSocket(server, historyService);
94
+
95
+ await new Promise((resolve, reject) => {
96
+ server.once('error', reject);
97
+ server.listen(port, runtimeOptions.host, resolve);
98
+ });
99
+
100
+ const homeUrl = runtimeOptions.publicUrl || `http://${clientIpAddress}:${port}`;
101
+
102
+ console.log(`ChatTransfer v${pkg.version}`);
103
+ console.log(`Server: ${homeUrl}`);
104
+ console.log(`Listen: ${runtimeOptions.host}:${port}`);
105
+ console.log(`CacheDir: ${uploadsDir}`);
106
+ console.log(`Runtime: ${getRuntimeName()}`);
107
+ console.log(`DataDir: ${path.dirname(getConfigFile())}`);
108
+
109
+ trackEvent('app_start', {
110
+ local_url: homeUrl,
111
+ port
112
+ });
113
+
114
+ if (runtimeOptions.openBrowser) {
115
+ openDefaultBrowser(homeUrl, (error) => {
116
+ if (error) {
117
+ console.error(`Failed to open browser: ${error.message}`);
118
+ }
119
+ });
120
+ }
121
+
122
+ return server;
123
+ }
124
+
125
+ if (require.main === module) {
126
+ start();
127
+ }
128
+
129
+ module.exports = {
130
+ createApp,
131
+ start
132
+ };
@@ -0,0 +1,75 @@
1
+ const ONLINE = 'ONLINE';
2
+ const OFFLINE = 'OFFLINE';
3
+
4
+ class RoomManager {
5
+ constructor() {
6
+ this.users = new Map();
7
+ this.groupName = '';
8
+ }
9
+
10
+ getOnline() {
11
+ return ONLINE;
12
+ }
13
+
14
+ getOffline() {
15
+ return OFFLINE;
16
+ }
17
+
18
+ setOnline({ username, socketId, nickname = '', ip = '' }) {
19
+ const existing = this.users.get(username) || {};
20
+ this.users.set(username, {
21
+ socketId,
22
+ status: ONLINE,
23
+ nickname: nickname || existing.nickname || '',
24
+ ip
25
+ });
26
+ }
27
+
28
+ getUser(username) {
29
+ return this.users.get(username);
30
+ }
31
+
32
+ setNickname(username, nickname) {
33
+ const user = this.users.get(username);
34
+
35
+ if (!user) {
36
+ return false;
37
+ }
38
+
39
+ user.nickname = nickname || '';
40
+ return true;
41
+ }
42
+
43
+ setOffline(username) {
44
+ const user = this.users.get(username);
45
+
46
+ if (!user) {
47
+ return false;
48
+ }
49
+
50
+ user.status = OFFLINE;
51
+ return true;
52
+ }
53
+
54
+ listUsers() {
55
+ return [...this.users.entries()].map(([username, user]) => ({
56
+ sender: username,
57
+ nickname: user.nickname || '',
58
+ ip: user.ip || '',
59
+ status: user.status
60
+ }));
61
+ }
62
+
63
+ setGroupName(groupName) {
64
+ this.groupName = groupName || '';
65
+ }
66
+
67
+ getGroupName() {
68
+ return this.groupName;
69
+ }
70
+ }
71
+
72
+ module.exports = {
73
+ RoomManager,
74
+ USER_STATUS: [ONLINE, OFFLINE]
75
+ };
@@ -0,0 +1,24 @@
1
+
2
+ /**
3
+ * 检查登录状态
4
+ * @param {*} app
5
+ * @param {*}
6
+ */
7
+ module.exports = (app) => {
8
+ app.get('/checkLogin', async (req, res) => {
9
+
10
+ try {
11
+ let response = {
12
+ code: 0 ,
13
+ message: '检查成功!',
14
+ data: {
15
+ users:global.users
16
+ }
17
+ }
18
+ res.send(response);
19
+ } catch (err) {
20
+ console.log(err);
21
+ res.status(500).send(err);
22
+ }
23
+ });
24
+ }
@@ -0,0 +1,49 @@
1
+ const express = require('express');
2
+ const path = require('path');
3
+ const router = express.Router();
4
+ const { getCachePath } = require('../config');
5
+
6
+ function sanitizeFileName(name) {
7
+ return path.basename(name || '').replace(/[\\/:*?"<>|\u0000]/g, '_');
8
+ }
9
+
10
+ router.post('/uploadFile', async (req, res) => {
11
+ const uploadsDir = getCachePath();
12
+ const files = req.files && (req.files.file_data || req.files.fileObj);
13
+
14
+ if (!files) {
15
+ return res.status(400).send({
16
+ code: -1,
17
+ message: 'No file uploaded'
18
+ });
19
+ }
20
+
21
+ const fileList = Array.isArray(files) ? files : [files];
22
+ const uploadResults = [];
23
+
24
+ for (const file of fileList) {
25
+ const timestamp = Date.now();
26
+ const safeName = `${timestamp}_${sanitizeFileName(file.name)}`;
27
+ const filePath = path.join(uploadsDir, 'files', safeName);
28
+
29
+ await new Promise((resolve, reject) => {
30
+ file.mv(filePath, (error) => (error ? reject(error) : resolve()));
31
+ });
32
+
33
+ uploadResults.push({
34
+ name: safeName,
35
+ mimetype: file.mimetype,
36
+ size: file.size,
37
+ fileUrl: `${req.protocol}://${req.headers.host}/files/${encodeURIComponent(safeName)}`,
38
+ localFilePath: filePath
39
+ });
40
+ }
41
+
42
+ res.send({
43
+ code: 0,
44
+ message: fileList.length > 1 ? 'Files uploaded' : 'File uploaded',
45
+ data: fileList.length > 1 ? uploadResults : uploadResults[0]
46
+ });
47
+ });
48
+
49
+ module.exports = router;
@@ -0,0 +1,37 @@
1
+ const { geneQR } = require("../tool");
2
+
3
+ /**
4
+ * 获取用户主目录
5
+ * @param {*} app
6
+ * @param {*} homeDir
7
+ */
8
+ module.exports = (app,homeUrl) => {
9
+ app.get('/geneBUserQR', async (req, res) => {
10
+
11
+ try {
12
+ const fullBUserUrl = homeUrl + "/scanLogin?fromUser=B"
13
+ let response = {
14
+ code: 0 ,
15
+ message: '创建成功!',
16
+ data: {
17
+ qr:""
18
+ }
19
+ }
20
+ const qrABase64Str = geneQR(fullBUserUrl);
21
+ if (qrABase64Str) {
22
+ response.data.qr = qrABase64Str
23
+ }else {
24
+ response.data = {};
25
+ response.code = -1;
26
+ response.message = "生成二维码失败";
27
+ }
28
+
29
+ console.log('response:',response);
30
+ res.send(response);
31
+
32
+ } catch (err) {
33
+ console.log(err);
34
+ res.status(500).send(err);
35
+ }
36
+ });
37
+ }
@@ -0,0 +1,39 @@
1
+ const { geneQR } = require("../tool");
2
+
3
+ /**
4
+ * 获取用户主目录
5
+ * @param {*} app
6
+ * @param {*} homeDir
7
+ */
8
+ module.exports = (app) => {
9
+ app.get('/geneQR', async (req, res) => {
10
+
11
+ try {
12
+ const qrContent = req.query.qrStr;
13
+ let response = {
14
+ code: 0 ,
15
+ message: '创建成功!',
16
+ data: {
17
+ qr:"",
18
+ qrText:""
19
+ }
20
+ }
21
+ const qrABase64Str = geneQR(qrContent);
22
+ if (qrABase64Str) {
23
+ response.data.qr = qrABase64Str;
24
+ response.data.qrText = qrContent;
25
+ }else {
26
+ response.data = {};
27
+ response.code = -1;
28
+ response.message = "生成二维码失败";
29
+ }
30
+
31
+ console.log('response:',response);
32
+ res.send(response);
33
+
34
+ } catch (err) {
35
+ console.log(err);
36
+ res.status(500).send(err);
37
+ }
38
+ });
39
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 获取用户主目录 / 缓存目录信息
3
+ * @param {*} app
4
+ * @param {*} uploadsDir 当前生效缓存目录
5
+ */
6
+ const { getDefaultCachePath, isCustomized, CONFIG_FILE } = require('../config.js');
7
+
8
+ module.exports = (app, uploadsDir) => {
9
+ app.get('/getHomeDir', async (req, res) => {
10
+ try {
11
+ const response = {
12
+ code: 0,
13
+ message: '获取成功',
14
+ data: {
15
+ "uploadsDir": uploadsDir,
16
+ "defaultDir": getDefaultCachePath(),
17
+ "isCustomized": isCustomized(),
18
+ "configFile": CONFIG_FILE
19
+ }
20
+ }
21
+ res.send(response);
22
+ } catch (err) {
23
+ console.log(err);
24
+ res.status(500).send(err);
25
+ }
26
+ });
27
+ }