feat: Добавить кэширование ответов GET с использованием ETag для повышения производительности и улучшения обработки запросов; обновить обработку ошибок в API для более информативных ответов
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s

This commit is contained in:
2025-08-27 12:41:06 +07:00
parent b3eb991095
commit ec50fcab72
2 changed files with 62 additions and 16 deletions
+18 -15
View File
@@ -518,7 +518,8 @@ app.post('/api/domains', async (req, res) => {
if (etag) {
const current = await headS3ObjectEtag(FILE_KEY).catch(() => undefined);
if (current && current.replace(/\"/g, '"') !== String(etag)) {
return res.status(412).json({ message: 'Precondition Failed: ETag mismatch' });
const meta = await headMeta(FILE_KEY);
return sendError(res, 412, 'Precondition Failed: ETag mismatch', 'E_ETAG_MISMATCH', { currentEtag: current, meta });
}
}
} catch (e) {
@@ -555,11 +556,11 @@ app.get('/api/asns', async (req, res) => {
try {
if (countOnly === 'true') {
// только количество
const { total } = await streamPaginatedText({
key: 'bgp_data/asns.txt', q, offset: 0, limit: 0,
mapLine: (line) => ({})
});
const cacheKey = `asns:count:${q}`;
const cached = getCountOnlyCache(cacheKey);
if (cached != null) return res.json(format === 'std' ? { items: [], total: cached, meta: {} } : { total: cached });
const { total } = await streamPaginatedText({ key: 'bgp_data/asns.txt', q, offset: 0, limit: 0, mapLine: () => ({}) });
setCountOnlyCache(cacheKey, total);
return res.json(format === 'std' ? { items: [], total, meta: {} } : { total });
} else if (limit !== undefined) {
const { items, total } = await streamPaginatedText({
@@ -652,10 +653,11 @@ app.get('/api/domains-new', async (req, res) => {
try {
if (countOnly === 'true') {
const { total } = await streamPaginatedText({
key: 'bgp_data/domains_community.txt', q, offset: 0, limit: 0,
mapLine: (line) => ({})
});
const cacheKey = `domains-new:count:${q}`;
const cached = getCountOnlyCache(cacheKey);
if (cached != null) return res.json(format === 'std' ? { items: [], total: cached, meta: {} } : { total: cached });
const { total } = await streamPaginatedText({ key: 'bgp_data/domains_community.txt', q, offset: 0, limit: 0, mapLine: () => ({}) });
setCountOnlyCache(cacheKey, total);
return res.json(format === 'std' ? { items: [], total, meta: {} } : { total });
} else if (limit !== undefined) {
const { items, total } = await streamPaginatedText({
@@ -748,10 +750,11 @@ app.get('/api/ip-ranges', async (req, res) => {
try {
if (countOnly === 'true') {
const { total } = await streamPaginatedText({
key: 'bgp_data/ips.txt', q, offset: 0, limit: 0,
mapLine: (line) => ({})
});
const cacheKey = `ip-ranges:count:${q}`;
const cached = getCountOnlyCache(cacheKey);
if (cached != null) return res.json(format === 'std' ? { items: [], total: cached, meta: {} } : { total: cached });
const { total } = await streamPaginatedText({ key: 'bgp_data/ips.txt', q, offset: 0, limit: 0, mapLine: () => ({}) });
setCountOnlyCache(cacheKey, total);
return res.json(format === 'std' ? { items: [], total, meta: {} } : { total });
} else if (limit !== undefined) {
const { items, total } = await streamPaginatedText({
@@ -1219,7 +1222,7 @@ app.post('/api/locks/:resource', (req, res) => {
const now = Date.now();
const existing = locks.get(resource);
if (existing && existing.expiresAt > now && existing.owner !== owner) {
return res.status(423).json({ message: 'Resource is locked by another user', owner: existing.owner, expiresAt: existing.expiresAt });
return sendError(res, 423, 'Resource is locked by another user', 'E_RESOURCE_LOCKED', { owner: existing.owner, expiresAt: existing.expiresAt });
}
const expiresAt = now + Math.max(30, Math.min(600, Number(ttlSeconds) || 120)) * 1000;
locks.set(resource, { owner, expiresAt });