perf(api): кэш профиля и конфигурации, лимитер argon2, SHA-256 для client secret, trust proxy

This commit is contained in:
Denozordec committed 2026-10-23 16:54:05 +07:00
1 parent 107eddba9e
commit 30d331c633
19 files changed
+666 -72

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
/**
* Счётчик «поколения» данных пользователей: растёт при любой записи в
* users/user_apps/user_permissions через функции этого пакета. Кэши верхнего
* уровня сверяют поколение, поэтому блокировка и смена прав действуют сразу.
*/
let userDataGeneration = 0
export function getUserDataGeneration(): number {
return userDataGeneration
}
export function bumpUserDataGeneration(): void {
userDataGeneration += 1
}
+1
View File
@@ -270,6 +270,7 @@ export function healthCheck(sqlite: Sqlite): void {
export * from './schema/index.js'
export * from './tx.js'
export * from './generation.js'
export * from './housekeeping.js'
export * from './users.js'
export * from './settings.js'
+18
View File
@@ -10,7 +10,24 @@ import { portalSettings } from './schema/index.js'
const SETTINGS_ID = 'main'
/** Разобранный конфиг на соединение; сбрасывается в setAppSwitcherConfig. */
const switcherCache = new WeakMap<AppDb, AppSwitcherConfig>()
export function getAppSwitcherConfig(db: AppDb): AppSwitcherConfig {
const hit = switcherCache.get(db)
// клон: вызывающий код вправе мутировать результат
if (hit) return structuredClone(hit)
const config = loadAppSwitcherConfig(db)
switcherCache.set(db, config)
return structuredClone(config)
}
/** Сброс кэша (если БД правили в обход setAppSwitcherConfig). */
export function invalidateAppSwitcherConfig(db: AppDb): void {
switcherCache.delete(db)
}
function loadAppSwitcherConfig(db: AppDb): AppSwitcherConfig {
const row = db
.select()
.from(portalSettings)
@@ -51,5 +68,6 @@ export function setAppSwitcherConfig(
})
.run()
}
switcherCache.delete(db)
return normalized
}
+5
View File
@@ -1,6 +1,7 @@
import { and, desc, eq, gt, gte, isNull, or, sql } from 'drizzle-orm'
import { createHash, randomUUID } from 'node:crypto'
import type { AppDb } from './index.js'
import { bumpUserDataGeneration } from './generation.js'
import { withTransaction } from './tx.js'
import {
refreshSessions,
@@ -99,6 +100,7 @@ export function createUser(
updatedAt: now,
})
.run()
bumpUserDataGeneration()
return getUserById(db, id)!
}
@@ -127,11 +129,13 @@ export function updateUser(
})
.where(eq(users.id, id))
.run()
bumpUserDataGeneration()
return getUserById(db, id)
}
export function deleteUser(db: AppDb, id: string): boolean {
const result = db.delete(users).where(eq(users.id, id)).run()
bumpUserDataGeneration()
return result.changes > 0
}
@@ -161,6 +165,7 @@ export function setUserAccess(
.where(eq(users.id, userId))
.run()
})
bumpUserDataGeneration()
}
/** Поиск для каталога пользователей: только нужные колонки, LIKE и LIMIT в SQL. */