-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathapp.js
More file actions
128 lines (109 loc) · 4.67 KB
/
app.js
File metadata and controls
128 lines (109 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const express = require('express');
const cors = require('cors');
const path = require('path');
const crypto = require('crypto');
const session = require('express-session');
const SqliteStore = require('better-sqlite3-session-store')(session);
const db = require('./db');
const app = express();
const PORT = process.env.PORT || 3000;
const BASE_URL = process.env.BASE_URL || `http://localhost:${PORT}`;
// ── Middleware ────────────────────────────────────────────────────────
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Session (30-day cookie, SQLite-backed)
const SESSION_SECRET = process.env.SESSION_SECRET || crypto.randomBytes(32).toString('hex');
app.use(
session({
store: new SqliteStore({
client: db,
expired: {
clear: true,
intervalMs: 900000, // clean up expired sessions every 15 min
},
}),
secret: SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
httpOnly: true,
},
})
);
// 首页路由:已登录 → 功能页,未登录 → intro
function serveByAuth(req, res) {
if (req.session && req.session.userId) {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
} else {
res.sendFile(path.join(__dirname, 'public', 'intro.html'));
}
}
app.get('/', serveByAuth);
app.get('/index.html', serveByAuth);
// intro 的"开始使用"入口 — 标记来源后进入功能页
app.get('/app', (req, res) => {
if (req.session && req.session.userId) {
// 已登录直接进
return res.sendFile(path.join(__dirname, 'public', 'index.html'));
}
if (req.query.from === 'intro') {
// 从 intro 点过来,允许进入(扫码登录)
return res.sendFile(path.join(__dirname, 'public', 'index.html'));
}
// 其他情况跳回 intro
res.redirect('/');
});
// intro 页面始终可访问(已登录用户也能看)
app.get('/intro', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'intro.html'));
});
// 静态文件(排除 index.html,强制通过首页路由判断登录状态)
app.use(express.static(path.join(__dirname, 'public'), { index: false }));
// ── Routes ───────────────────────────────────────────────────────────
// New Phase-1 route mounts (files to be created in later phases)
try { app.use('/api/auth', require('./routes/auth')); } catch (e) { /* route not yet created */ }
try { app.use('/api/channels', require('./routes/channels')); } catch (e) { /* route not yet created */ }
try { app.use('/api/key', require('./routes/key')); } catch (e) { /* route not yet created */ }
try { app.use('/api/push-logs', require('./routes/push-logs')); } catch (e) { /* route not yet created */ }
try { app.use('/api/admin', require('./routes/admin')); } catch (e) { /* route not yet created */ }
try { app.use('/api/track', require('./routes/track')); } catch (e) { /* route not yet created */ }
// Existing routes (login.js removed — replaced by routes/auth.js)
app.use('/api/reminders', require('./routes/reminders'));
app.use('/', require('./routes/notify'));
// 提供 BASE_URL 给前端
app.get('/api/config', (req, res) => {
res.json({ base_url: BASE_URL });
});
// ── Startup ──────────────────────────────────────────────────────────
app.listen(PORT, () => {
console.log(`🚀 BotTalk 运行在 http://localhost:${PORT}`);
console.log(`📡 API 地址: ${BASE_URL}`);
// Start scheduler
require('./scheduler');
// Restore message pollers for all active channels with context_token
try {
const { startMessagePoller } = require('./services/message-poller');
const activeChannels = db
.prepare(
`SELECT c.id, c.bot_token, c.wechat_openid, c.context_token
FROM channels c
WHERE c.id IN (
SELECT MAX(id) FROM channels
WHERE context_token IS NOT NULL AND bot_token IS NOT NULL
GROUP BY wechat_openid
)`
)
.all();
for (const ch of activeChannels) {
console.log(`🔄 恢复频道轮询: channel=${ch.id} openid=${ch.wechat_openid}`);
startMessagePoller(ch.bot_token, ch.wechat_openid);
}
if (activeChannels.length > 0) {
console.log(`✅ 已恢复 ${activeChannels.length} 个频道的消息轮询`);
}
} catch (e) {
console.error('⚠️ 恢复频道轮询失败:', e.message);
}
});