Skip to content

Commit 86ba0e7

Browse files
committed
provide central settings ui
1 parent acea17a commit 86ba0e7

File tree

14 files changed

+663
-196
lines changed

14 files changed

+663
-196
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ bot.db
99
bot.db-shm
1010
bot.db-wal
1111
dist
12-
var
12+
var
13+
14+
# TypeScript incremental cache
15+
*.tsbuildinfo

AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ npm start # Build and run in production
2222
```bash
2323
npm test # Run TypeScript tests with mocha
2424
npm test:js # Run JavaScript tests
25-
npm run build:tsc # Compile with TypeScript compiler
25+
npm run build:tsc # Type-check with tsc (run before commits/CI)
2626
```
2727

28+
### Build Tools
29+
30+
- **esbuild** (`npm run build`, `npm run build:prod`): Fast bundling/transpiling, no type-checking
31+
- **tsc** (`npm run build:tsc`): Type-checking - run before commits to catch type errors
32+
2833
## Architecture Overview
2934

3035
### Main Entry Points

src/controller/dashboard_settings_controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ export class DashboardSettingsController extends BaseController {
4747
? { type: 'success', title: 'Settings saved.' }
4848
: undefined;
4949
res.render('dashboard/settings', {
50-
activePage: 'dashboard',
50+
activePage: 'settings',
51+
activeSettingsPage: 'dashboard',
5152
title: 'Dashboard Settings | Crypto Bot',
5253
config,
5354
exchanges: EXCHANGES,

src/controller/desks_controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export class DesksController extends BaseController {
2424
router.get('/desk', (req: any, res: any) => {
2525
const deskList = this.deskService.getDesks();
2626
res.render('desk/index', {
27-
activePage: 'desk',
27+
activePage: 'settings',
28+
activeSettingsPage: 'desk',
2829
title: 'Desk Settings | Crypto Bot',
2930
deskList
3031
});

src/controller/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ export { LogsController } from './logs_controller';
1010
export { DesksController } from './desks_controller';
1111
export { CcxtExchangesController } from './ccxt_exchanges_controller';
1212
export { DashboardSettingsController } from './dashboard_settings_controller';
13-
export { ProfileController } from '../profile/profile_controller';
13+
export { ProfileController } from './profile_controller';
1414
export { SettingsController } from './settings_controller';
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { BaseController, TemplateHelpers } from '../controller/base_controller';
2-
import { ProfileService } from './profile_service';
1+
import { BaseController, TemplateHelpers } from './base_controller';
2+
import { ProfileService } from '../profile/profile_service';
33
import { ProfilePairService } from '../modules/profile_pair_service';
4-
import { Profile } from './types';
4+
import { Profile } from '../profile/types';
55
import { StrategyRegistry } from '../modules/strategy/v2/strategy_registry';
66
import { CcxtCandleWatchService } from '../modules/system/ccxt_candle_watch_service';
77
import express from 'express';

src/controller/settings_controller.ts

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { BaseController, TemplateHelpers } from './base_controller';
22
import { ConfigService } from '../modules/system/config_service';
33
import express from 'express';
4+
import os from 'os';
5+
import fs from 'fs';
6+
import path from 'path';
47

58
export class SettingsController extends BaseController {
69
constructor(
@@ -11,26 +14,124 @@ export class SettingsController extends BaseController {
1114
}
1215

1316
registerRoutes(router: express.Router): void {
14-
router.get('/settings', this.getSettings.bind(this));
15-
router.post('/settings', this.saveSettings.bind(this));
17+
router.get('/settings', this.getIndex.bind(this));
18+
router.get('/settings/webserver', this.getWebserver.bind(this));
19+
router.post('/settings/webserver', this.saveWebserver.bind(this));
20+
router.get('/settings/notifications', this.getNotifications.bind(this));
21+
router.post('/settings/notifications', this.saveNotifications.bind(this));
1622
}
1723

18-
private getSettings(req: express.Request, res: express.Response): void {
24+
private getIndex(req: express.Request, res: express.Response): void {
25+
const sysInfo = this.getSystemInfo();
26+
27+
this.render(res, 'settings/index', {
28+
activePage: 'settings',
29+
activeSettingsPage: '',
30+
title: 'Settings | Crypto Bot',
31+
sysInfo
32+
});
33+
}
34+
35+
private getSystemInfo() {
36+
const totalMem = os.totalmem();
37+
const freeMem = os.freemem();
38+
const usedMem = totalMem - freeMem;
39+
const cpus = os.cpus();
40+
const uptimeSec = os.uptime();
41+
42+
const dbPath = path.join(this.configService['projectDir'], 'var', 'bot.db');
43+
let dbSize: number | null = null;
44+
try {
45+
dbSize = fs.statSync(dbPath).size;
46+
} catch (_) {}
47+
48+
let diskTotal: number | null = null;
49+
let diskFree: number | null = null;
50+
try {
51+
const stat = (fs as any).statfsSync('/');
52+
diskTotal = stat.blocks * stat.bsize;
53+
diskFree = stat.bfree * stat.bsize;
54+
} catch (_) {}
55+
56+
return {
57+
memory: {
58+
total: totalMem,
59+
used: usedMem,
60+
free: freeMem,
61+
usedPercent: Math.round((usedMem / totalMem) * 100)
62+
},
63+
cpu: {
64+
count: cpus.length,
65+
model: cpus[0]?.model?.replace(/\s+/g, ' ').trim() ?? 'Unknown'
66+
},
67+
disk: diskTotal !== null && diskFree !== null
68+
? {
69+
total: diskTotal,
70+
free: diskFree,
71+
used: diskTotal - diskFree,
72+
usedPercent: Math.round(((diskTotal - diskFree) / diskTotal) * 100)
73+
}
74+
: null,
75+
db: {
76+
path: dbPath,
77+
size: dbSize
78+
},
79+
uptime: uptimeSec,
80+
nodeVersion: process.version,
81+
platform: os.platform()
82+
};
83+
}
84+
85+
private getWebserver(req: express.Request, res: express.Response): void {
1986
const settings = this.configService.getBotSettings();
2087
const saved = req.query.saved === '1';
2188

22-
this.render(res, 'settings', {
89+
this.render(res, 'settings/webserver', {
2390
activePage: 'settings',
24-
title: 'Settings | Crypto Bot',
91+
activeSettingsPage: 'webserver',
92+
title: 'Webserver Settings | Crypto Bot',
2593
settings,
2694
saved
2795
});
2896
}
2997

30-
private saveSettings(req: express.Request, res: express.Response): void {
98+
private saveWebserver(req: express.Request, res: express.Response): void {
3199
const body = req.body;
100+
const current = this.configService.getBotSettings();
32101

33102
const settings = {
103+
...current,
104+
webserver: {
105+
ip: this.nullIfEmpty(body.webserver_ip),
106+
port: this.parseIntOrNull(body.webserver_port),
107+
username: this.nullIfEmpty(body.webserver_username),
108+
password: this.nullIfEmpty(body.webserver_password)
109+
}
110+
};
111+
112+
this.configService.saveBotSettings(settings);
113+
res.redirect('/settings/webserver?saved=1');
114+
}
115+
116+
private getNotifications(req: express.Request, res: express.Response): void {
117+
const settings = this.configService.getBotSettings();
118+
const saved = req.query.saved === '1';
119+
120+
this.render(res, 'settings/notifications', {
121+
activePage: 'settings',
122+
activeSettingsPage: 'notifications',
123+
title: 'Notification Settings | Crypto Bot',
124+
settings,
125+
saved
126+
});
127+
}
128+
129+
private saveNotifications(req: express.Request, res: express.Response): void {
130+
const body = req.body;
131+
const current = this.configService.getBotSettings();
132+
133+
const settings = {
134+
...current,
34135
notify: {
35136
slack: {
36137
webhook: this.nullIfEmpty(body.slack_webhook),
@@ -48,17 +149,11 @@ export class SettingsController extends BaseController {
48149
chat_id: this.nullIfEmpty(body.telegram_chat_id),
49150
token: this.nullIfEmpty(body.telegram_token)
50151
}
51-
},
52-
webserver: {
53-
ip: this.nullIfEmpty(body.webserver_ip),
54-
port: this.parseIntOrNull(body.webserver_port),
55-
username: this.nullIfEmpty(body.webserver_username),
56-
password: this.nullIfEmpty(body.webserver_password)
57152
}
58153
};
59154

60155
this.configService.saveBotSettings(settings);
61-
res.redirect('/settings?saved=1');
156+
res.redirect('/settings/notifications?saved=1');
62157
}
63158

64159
private nullIfEmpty(value: string | undefined): string | null {

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"allowJs": true,
1515
"checkJs": false,
1616
"importHelpers": true,
17-
"noEmit": true
17+
"noEmit": true,
18+
"incremental": true
1819
},
1920
"include": ["index.ts", "src/**/*"],
2021
"exclude": ["node_modules", "dist"],
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<div class="w-52 shrink-0">
2+
<nav class="bg-white rounded shadow overflow-hidden text-sm">
3+
<div class="px-3 py-2 bg-gray-50 border-b border-gray-200">
4+
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wide">Info</span>
5+
</div>
6+
<a href="/settings"
7+
class="flex items-center gap-2 px-3 py-2 border-b border-gray-100 border-l-2 <%= activeSettingsPage === '' ? 'bg-blue-50 text-blue-700 font-medium border-l-blue-600' : 'text-gray-700 hover:bg-gray-50 border-l-transparent' %>">
8+
<i class="fas fa-info-circle w-4 text-center text-xs <%= activeSettingsPage === '' ? 'text-blue-500' : 'text-gray-400' %>"></i>
9+
System
10+
</a>
11+
12+
<div class="px-3 py-2 bg-gray-50 border-b border-t border-gray-200">
13+
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wide">Settings</span>
14+
</div>
15+
<a href="/settings/webserver"
16+
class="flex items-center gap-2 px-3 py-2 border-b border-gray-100 border-l-2 <%= activeSettingsPage === 'webserver' ? 'bg-blue-50 text-blue-700 font-medium border-l-blue-600' : 'text-gray-700 hover:bg-gray-50 border-l-transparent' %>">
17+
<i class="fas fa-server w-4 text-center text-xs <%= activeSettingsPage === 'webserver' ? 'text-blue-500' : 'text-gray-400' %>"></i>
18+
Webserver
19+
</a>
20+
<a href="/settings/notifications"
21+
class="flex items-center gap-2 px-3 py-2 border-b border-gray-100 border-l-2 <%= activeSettingsPage === 'notifications' ? 'bg-blue-50 text-blue-700 font-medium border-l-blue-600' : 'text-gray-700 hover:bg-gray-50 border-l-transparent' %>">
22+
<i class="fas fa-bell w-4 text-center text-xs <%= activeSettingsPage === 'notifications' ? 'text-blue-500' : 'text-gray-400' %>"></i>
23+
Notifications
24+
</a>
25+
26+
<div class="px-3 py-2 bg-gray-50 border-b border-t border-gray-200">
27+
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wide">Dashboard</span>
28+
</div>
29+
<a href="/dashboard/settings"
30+
class="flex items-center gap-2 px-3 py-2 border-b border-gray-100 border-l-2 <%= activeSettingsPage === 'dashboard' ? 'bg-blue-50 text-blue-700 font-medium border-l-blue-600' : 'text-gray-700 hover:bg-gray-50 border-l-transparent' %>">
31+
<i class="fas fa-chart-bar w-4 text-center text-xs <%= activeSettingsPage === 'dashboard' ? 'text-blue-500' : 'text-gray-400' %>"></i>
32+
Settings
33+
</a>
34+
35+
<div class="px-3 py-2 bg-gray-50 border-b border-t border-gray-200">
36+
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wide">Desk</span>
37+
</div>
38+
<a href="/desk"
39+
class="flex items-center gap-2 px-3 py-2 border-l-2 <%= activeSettingsPage === 'desk' ? 'bg-blue-50 text-blue-700 font-medium border-l-blue-600' : 'text-gray-700 hover:bg-gray-50 border-l-transparent' %>">
40+
<i class="fas fa-desktop w-4 text-center text-xs <%= activeSettingsPage === 'desk' ? 'text-blue-500' : 'text-gray-400' %>"></i>
41+
Desks
42+
</a>
43+
</nav>
44+
</div>

0 commit comments

Comments
 (0)