server.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import express from 'express';
  2. import { readFileSync } from 'node:fs';
  3. // @ts-ignore
  4. import { XMLHttpRequest } from 'xmlhttprequest';
  5. // @ts-ignore
  6. globalThis.XMLHttpRequest = XMLHttpRequest;
  7. // Polyfill localStorage for Parse SDK
  8. const memoryStorage = {
  9. _data: new Map<string, string>(),
  10. getItem(key: string) {
  11. return this._data.get(key) || null;
  12. },
  13. setItem(key: string, value: string) {
  14. this._data.set(key, String(value));
  15. },
  16. removeItem(key: string) {
  17. this._data.delete(key);
  18. },
  19. clear() {
  20. this._data.clear();
  21. },
  22. key(index: number) {
  23. return Array.from(this._data.keys())[index] || null;
  24. },
  25. get length() {
  26. return this._data.size;
  27. }
  28. };
  29. // @ts-ignore
  30. globalThis.localStorage = memoryStorage;
  31. /**
  32. * 注意:生产/客户实例正式入口是 fmode-server 加载 api/routes.ts。
  33. * 本 server.ts 仅作本地开发壳,不挂载直连上游的 Sorftime/TikHub token。
  34. */
  35. function getConfig() {
  36. try {
  37. const configPath = process.env.NODE_ENV === 'test' ? './config.test.json' : './config.json';
  38. const configContent = readFileSync(configPath, 'utf-8');
  39. return JSON.parse(configContent);
  40. } catch (error: unknown) {
  41. const errorMessage = error instanceof Error ? error.message : 'Unknown error';
  42. console.warn('Config file not found, using defaults:', errorMessage);
  43. return {
  44. parse: {
  45. port: 3000,
  46. appId: 'TARGET_PARSE_APP_ID',
  47. serverURL: 'http://localhost:3000/parse'
  48. }
  49. };
  50. }
  51. }
  52. const app = express();
  53. app.use(express.json());
  54. app.use(express.urlencoded({ extended: true }));
  55. async function startServer() {
  56. const config = getConfig();
  57. const PORT = process.env.PORT || config.parse?.port || 3000;
  58. const Parse = require('parse');
  59. Parse.initialize(config.parse.appId);
  60. Parse.serverURL = `http://localhost:${PORT}/parse`;
  61. Parse.masterKey = config.parse.masterKey;
  62. // @ts-ignore
  63. globalThis.Parse = Parse;
  64. app.use((req: any, res: any, next: () => void) => {
  65. console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
  66. next();
  67. });
  68. app.get('/health', (req: any, res: any) => {
  69. res.json({
  70. status: 'healthy',
  71. instance: 'TARGET-voc-instance',
  72. timestamp: new Date().toISOString(),
  73. uptime: process.uptime()
  74. });
  75. });
  76. app.get('/', (req: any, res: any) => {
  77. res.json({
  78. message: 'TARGET VOC Instance - dev shell',
  79. version: '1.0.0-customer-relay',
  80. note: 'Production uses fmode-server + api/routes.ts',
  81. endpoints: ['/api', '/health', '/parse']
  82. });
  83. });
  84. // 开发壳可选挂载 api/routes(正式环境由 fmode-server 自动扫描)
  85. try {
  86. const apiRouter = (await import('./api/routes.ts')).default;
  87. app.use('/api', apiRouter);
  88. console.log('[DevShell] /api mounted from api/routes.ts');
  89. } catch (e: any) {
  90. console.warn('[DevShell] api/routes.ts not mounted in this runtime:', e?.message || e);
  91. }
  92. app.use((req: any, res: any) => {
  93. res.status(404).json({
  94. message: 'Not Found',
  95. path: req.path,
  96. method: req.method
  97. });
  98. });
  99. app.use((err: any, req: any, res: any, next: any) => {
  100. console.error('Error:', err?.message || err);
  101. res.status(500).json({
  102. message: 'Internal Server Error',
  103. error: err?.message
  104. });
  105. });
  106. app.listen(PORT, () => {
  107. console.log(`[TARGET-voc-instance] listening on :${PORT}`);
  108. });
  109. }
  110. startServer().catch((err) => {
  111. console.error('Failed to start server:', err);
  112. process.exit(1);
  113. });