const express = require('express');
const crypto = require('crypto');
const authMiddleware = require('../middleware/auth');
const {
DesktopSessionError,
createDesktopSession,
createSsoTicket,
exchangeSsoTicket,
getDesktopBootstrap,
revokeDesktopSession,
} = require('../services/desktopSessionService');
const router = express.Router();
function requestUserId(req) {
return req.user?.sub ?? req.user?.id;
}
function sendDesktopSessionError(res, error, fallbackMessage) {
if (error instanceof DesktopSessionError) {
return res.status(error.status).json({
success: false,
error: error.message,
code: error.code,
});
}
console.error(fallbackMessage, error);
return res.status(500).json({ success: false, error: fallbackMessage });
}
function safeScriptJson(value) {
return JSON.stringify(value)
.replace(//g, '\\u003e')
.replace(/&/g, '\\u0026')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
}
function sendSsoExchangeHtml(res, result) {
const nonce = crypto.randomBytes(18).toString('base64');
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, private');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Referrer-Policy', 'no-referrer');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader(
'Content-Security-Policy',
`default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; base-uri 'none'; form-action 'none'; frame-ancestors 'self'`
);
res.type('html').send(`
正在进入总控
正在进入总控...
`);
}
router.post('/session', authMiddleware, async (req, res) => {
try {
if (req.user?.tokenType) {
throw new DesktopSessionError('请使用用户登录令牌创建桌面会话', 401, 'DESKTOP_USER_TOKEN_REQUIRED');
}
const data = await createDesktopSession({
userId: requestUserId(req),
payload: req.body || {},
});
res.setHeader('Cache-Control', 'no-store');
return res.json({ success: true, data });
} catch (error) {
return sendDesktopSessionError(res, error, '创建桌面会话失败');
}
});
router.get('/bootstrap', authMiddleware, async (req, res) => {
try {
const data = await getDesktopBootstrap({
claims: req.user,
clientId: req.query.clientId,
});
res.setHeader('Cache-Control', 'no-store');
return res.json({ success: true, data });
} catch (error) {
return sendDesktopSessionError(res, error, '读取桌面启动配置失败');
}
});
router.post('/sso-ticket', authMiddleware, async (req, res) => {
try {
const data = await createSsoTicket({
claims: req.user,
clientId: req.body?.clientId,
redirectPath: req.body?.redirectPath,
});
res.setHeader('Cache-Control', 'no-store');
return res.json({ success: true, data });
} catch (error) {
return sendDesktopSessionError(res, error, '创建总控登录票据失败');
}
});
router.get('/sso/exchange', async (req, res) => {
try {
const result = await exchangeSsoTicket(req.query.ticket);
return sendSsoExchangeHtml(res, result);
} catch (error) {
if (error instanceof DesktopSessionError) {
res.setHeader('Cache-Control', 'no-store');
return res.status(error.status).type('html').send('登录失效登录票据无效或已过期,请返回桌面客户端重试。');
}
console.error('交换总控登录票据失败', error);
return res.status(500).type('html').send('登录失败总控登录失败,请稍后重试。');
}
});
router.post('/session/revoke', authMiddleware, async (req, res) => {
try {
const data = await revokeDesktopSession(req.user);
res.setHeader('Cache-Control', 'no-store');
return res.json({ success: true, data });
} catch (error) {
return sendDesktopSessionError(res, error, '退出桌面会话失败');
}
});
module.exports = router;