opex792 commited on
Commit
8f05c3b
·
verified ·
1 Parent(s): 930a5c7

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +109 -19
server.js CHANGED
@@ -1,8 +1,12 @@
 
 
1
  import express from 'express';
2
  import swaggerUi from 'swagger-ui-express';
3
  import YAML from 'yamljs';
4
  import path from 'path';
5
  import { fileURLToPath } from 'url';
 
 
6
  import { getAuthorData, getAuthorFilters } from 'stihirus-reader';
7
 
8
  const __filename = fileURLToPath(import.meta.url);
@@ -10,16 +14,68 @@ const __dirname = path.dirname(__filename);
10
 
11
  const app = express();
12
  const port = process.env.PORT || 7860;
 
 
13
 
14
  const swaggerDocument = YAML.load(path.join(__dirname, 'openapi.yaml'));
15
 
16
  app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  app.get('/author/:identifier', async (req, res) => {
19
  const identifier = req.params.identifier;
20
  let page = req.query.page;
21
  let delay = req.query.delay;
22
-
23
  let pageNum = null;
24
  if (page !== undefined) {
25
  const parsedPage = parseInt(page, 10);
@@ -31,7 +87,6 @@ app.get('/author/:identifier', async (req, res) => {
31
  return res.status(400).json({ status: 'error', error: { code: 400, message: 'Invalid page parameter. Use null, 0, or a positive integer.' } });
32
  }
33
  }
34
-
35
  let delayMs = undefined;
36
  if (delay !== undefined) {
37
  const parsedDelay = parseInt(delay, 10);
@@ -41,32 +96,61 @@ app.get('/author/:identifier', async (req, res) => {
41
  return res.status(400).json({ status: 'error', error: { code: 400, message: 'Invalid delay parameter. Use a non-negative integer.' } });
42
  }
43
  }
44
-
 
45
  try {
46
- const result = await getAuthorData(identifier, pageNum, delayMs);
47
- if (result.status === 'success') {
48
- res.json(result);
 
 
 
 
 
49
  } else {
50
- res.status(result.error.code >= 400 && result.error.code < 600 ? result.error.code : 500).json(result);
 
 
 
 
51
  }
52
  } catch (err) {
53
- console.error(`Error processing /author/${identifier}:`, err);
54
- res.status(500).json({ status: 'error', error: { code: 500, message: 'Internal Server Error', originalMessage: err.message } });
 
 
 
 
55
  }
56
  });
57
 
58
  app.get('/author/:identifier/filters', async (req, res) => {
59
  const identifier = req.params.identifier;
60
- try {
61
- const result = await getAuthorFilters(identifier);
62
- if (result.status === 'success') {
63
- res.json(result);
 
 
 
 
 
 
 
64
  } else {
65
- res.status(result.error.code >= 400 && result.error.code < 600 ? result.error.code : 500).json(result);
 
 
 
 
66
  }
67
  } catch (err) {
68
- console.error(`Error processing /author/${identifier}/filters:`, err);
69
- res.status(500).json({ status: 'error', error: { code: 500, message: 'Internal Server Error', originalMessage: err.message } });
 
 
 
 
70
  }
71
  });
72
 
@@ -74,7 +158,13 @@ app.get('/', (req, res) => {
74
  res.redirect('/docs');
75
  });
76
 
77
- app.listen(port, () => {
78
- console.log(`StihiRus API wrapper listening on port ${port}`);
79
- console.log(`API Docs available at /docs`);
 
 
 
 
 
 
80
  });
 
1
+ server.js
2
+ ```javascript
3
  import express from 'express';
4
  import swaggerUi from 'swagger-ui-express';
5
  import YAML from 'yamljs';
6
  import path from 'path';
7
  import { fileURLToPath } from 'url';
8
+ import fs from 'fs/promises';
9
+ import crypto from 'crypto';
10
  import { getAuthorData, getAuthorFilters } from 'stihirus-reader';
11
 
12
  const __filename = fileURLToPath(import.meta.url);
 
14
 
15
  const app = express();
16
  const port = process.env.PORT || 7860;
17
+ const CACHE_DIR = path.join(__dirname, 'cache');
18
+ const CACHE_DURATION_MS = 60 * 60 * 1000;
19
 
20
  const swaggerDocument = YAML.load(path.join(__dirname, 'openapi.yaml'));
21
 
22
  app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
23
 
24
+ async function ensureCacheDir() {
25
+ try {
26
+ await fs.mkdir(CACHE_DIR, { recursive: true });
27
+ } catch (err) {
28
+ console.error('Error creating cache directory:', err);
29
+ }
30
+ }
31
+
32
+ function generateCacheKey(prefix, identifier, queryParams = {}) {
33
+ const identifierPart = String(identifier).replace(/[^a-zA-Z0-9_-]/g, '_');
34
+ let queryPart = '';
35
+ const sortedKeys = Object.keys(queryParams).sort();
36
+ sortedKeys.forEach(key => {
37
+ if (queryParams[key] !== undefined && queryParams[key] !== null) {
38
+ queryPart += `_${key}_${String(queryParams[key])}`;
39
+ } else if (queryParams[key] === null) {
40
+ queryPart += `_${key}_null`;
41
+ }
42
+ });
43
+ return `${prefix}_${identifierPart}${queryPart}.json`;
44
+ }
45
+
46
+ async function readCache(key) {
47
+ const filePath = path.join(CACHE_DIR, key);
48
+ try {
49
+ const stats = await fs.stat(filePath);
50
+ const now = Date.now();
51
+ const mtime = stats.mtime.getTime();
52
+ const isStale = (now - mtime) > CACHE_DURATION_MS;
53
+ const content = await fs.readFile(filePath, 'utf-8');
54
+ const data = JSON.parse(content);
55
+ return { data, isStale, exists: true, mtime };
56
+ } catch (err) {
57
+ if (err.code === 'ENOENT') {
58
+ return { exists: false };
59
+ }
60
+ console.error(`Error reading cache file ${key}:`, err);
61
+ return { exists: false, error: true };
62
+ }
63
+ }
64
+
65
+ async function writeCache(key, data) {
66
+ const filePath = path.join(CACHE_DIR, key);
67
+ try {
68
+ const content = JSON.stringify(data);
69
+ await fs.writeFile(filePath, content, 'utf-8');
70
+ } catch (err) {
71
+ console.error(`Error writing cache file ${key}:`, err);
72
+ }
73
+ }
74
+
75
  app.get('/author/:identifier', async (req, res) => {
76
  const identifier = req.params.identifier;
77
  let page = req.query.page;
78
  let delay = req.query.delay;
 
79
  let pageNum = null;
80
  if (page !== undefined) {
81
  const parsedPage = parseInt(page, 10);
 
87
  return res.status(400).json({ status: 'error', error: { code: 400, message: 'Invalid page parameter. Use null, 0, or a positive integer.' } });
88
  }
89
  }
 
90
  let delayMs = undefined;
91
  if (delay !== undefined) {
92
  const parsedDelay = parseInt(delay, 10);
 
96
  return res.status(400).json({ status: 'error', error: { code: 400, message: 'Invalid delay parameter. Use a non-negative integer.' } });
97
  }
98
  }
99
+ const cacheKey = generateCacheKey('author', identifier, { page: pageNum });
100
+ let cacheEntry = null;
101
  try {
102
+ cacheEntry = await readCache(cacheKey);
103
+ if (cacheEntry.exists && !cacheEntry.isStale) {
104
+ return res.json(cacheEntry.data);
105
+ }
106
+ const freshResult = await getAuthorData(identifier, pageNum, delayMs);
107
+ if (freshResult.status === 'success') {
108
+ await writeCache(cacheKey, freshResult);
109
+ return res.json(freshResult);
110
  } else {
111
+ if (cacheEntry.exists) {
112
+ return res.json(cacheEntry.data);
113
+ } else {
114
+ return res.status(freshResult.error.code >= 400 && freshResult.error.code < 600 ? freshResult.error.code : 500).json(freshResult);
115
+ }
116
  }
117
  } catch (err) {
118
+ if (cacheEntry && cacheEntry.exists) {
119
+ return res.json(cacheEntry.data);
120
+ } else {
121
+ console.error(`Error processing /author/${identifier} (CacheKey: ${cacheKey}):`, err);
122
+ return res.status(500).json({ status: 'error', error: { code: 500, message: 'Internal Server Error', originalMessage: err.message } });
123
+ }
124
  }
125
  });
126
 
127
  app.get('/author/:identifier/filters', async (req, res) => {
128
  const identifier = req.params.identifier;
129
+ const cacheKey = generateCacheKey('filters', identifier);
130
+ let cacheEntry = null;
131
+ try {
132
+ cacheEntry = await readCache(cacheKey);
133
+ if (cacheEntry.exists && !cacheEntry.isStale) {
134
+ return res.json(cacheEntry.data);
135
+ }
136
+ const freshResult = await getAuthorFilters(identifier);
137
+ if (freshResult.status === 'success') {
138
+ await writeCache(cacheKey, freshResult);
139
+ return res.json(freshResult);
140
  } else {
141
+ if (cacheEntry.exists) {
142
+ return res.json(cacheEntry.data);
143
+ } else {
144
+ return res.status(freshResult.error.code >= 400 && freshResult.error.code < 600 ? freshResult.error.code : 500).json(freshResult);
145
+ }
146
  }
147
  } catch (err) {
148
+ if (cacheEntry && cacheEntry.exists) {
149
+ return res.json(cacheEntry.data);
150
+ } else {
151
+ console.error(`Error processing /author/${identifier}/filters (CacheKey: ${cacheKey}):`, err);
152
+ return res.status(500).json({ status: 'error', error: { code: 500, message: 'Internal Server Error', originalMessage: err.message } });
153
+ }
154
  }
155
  });
156
 
 
158
  res.redirect('/docs');
159
  });
160
 
161
+ ensureCacheDir().then(() => {
162
+ app.listen(port, () => {
163
+ console.log(`StihiRus API wrapper listening on port ${port}`);
164
+ console.log(`Cache directory: ${CACHE_DIR}`);
165
+ console.log(`API Docs available at /docs`);
166
+ });
167
+ }).catch(err => {
168
+ console.error("Failed to initialize cache directory. Exiting.", err);
169
+ process.exit(1);
170
  });