chore: update docker
This commit is contained in:
@@ -1,18 +1,13 @@
|
|||||||
import { env, getVaultToken } from '../config/env';
|
import{env}from '../config/env';
|
||||||
import { vaultAppRoleLogin } from '../config/vault';
|
import{vaultAppRoleLogin}from '../config/vault';
|
||||||
import { fetchJwtKeysFromVault, swapKeyMap, getKeyMapSize } from './jwt.service';
|
import{fetchJwtKeysFromVault,swapKeyMap,getKeyMapSize}from './jwt.service';
|
||||||
import { fetchCsrfConfig, swapCsrfConfig } from './csrf.service';
|
import{fetchCsrfConfig,swapCsrfConfig}from './csrf.service';
|
||||||
import { logger } from '../lib/logger';
|
import{logger}from '../lib/logger';
|
||||||
|
|
||||||
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
|
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
||||||
|
|
||||||
let timer: NodeJS.Timeout | null = null;
|
let timer: NodeJS.Timeout | null = null;
|
||||||
let currentVaultToken: string | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Atomic refresh: pre-fetch JWT keys + CSRF config, swap globals only if BOTH succeed.
|
|
||||||
* При любой ошибке оставляем старые значения в памяти, сервис продолжает работать.
|
|
||||||
*/
|
|
||||||
export async function refreshAllKeys(): Promise<void> {
|
export async function refreshAllKeys(): Promise<void> {
|
||||||
const { addr, roleId, secretId, mount, jwtKidPath, jwtKidsPrefix, csrfPath } = env.vault;
|
const { addr, roleId, secretId, mount, jwtKidPath, jwtKidsPrefix, csrfPath } = env.vault;
|
||||||
|
|
||||||
@@ -21,25 +16,17 @@ export async function refreshAllKeys(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vault token: используем закэшированный из initEnv, либо логинимся заново
|
const token = await vaultAppRoleLogin(addr, roleId, secretId);
|
||||||
let token = currentVaultToken || getVaultToken();
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
const fresh = await vaultAppRoleLogin(addr, roleId, secretId);
|
logger.error('Key refresh: Vault AppRole login failed');
|
||||||
if (!fresh) {
|
return;
|
||||||
logger.error('Key refresh: Vault AppRole login failed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
token = fresh;
|
|
||||||
currentVaultToken = fresh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Pre-fetch обоих секретов параллельно (НЕ мутируя глобал) ───────────
|
|
||||||
const jwtPromise = fetchJwtKeysFromVault(addr, token, mount, jwtKidPath, jwtKidsPrefix);
|
const jwtPromise = fetchJwtKeysFromVault(addr, token, mount, jwtKidPath, jwtKidsPrefix);
|
||||||
const csrfPromise = csrfPath ? fetchCsrfConfig(addr, token, mount, csrfPath) : Promise.resolve(null);
|
const csrfPromise = csrfPath ? fetchCsrfConfig(addr, token, mount, csrfPath) : Promise.resolve(null);
|
||||||
|
|
||||||
const [jwtResult, csrfResult] = await Promise.allSettled([jwtPromise, csrfPromise]);
|
const [jwtResult, csrfResult] = await Promise.allSettled([jwtPromise, csrfPromise]);
|
||||||
|
|
||||||
// ── Атомарность: если хоть один обязательный fetch упал — НИЧЕГО не меняем ──
|
|
||||||
if (jwtResult.status === 'rejected') {
|
if (jwtResult.status === 'rejected') {
|
||||||
logger.error(`Key refresh ABORTED — JWT keys fetch failed: ${jwtResult.reason?.message || jwtResult.reason}`);
|
logger.error(`Key refresh ABORTED — JWT keys fetch failed: ${jwtResult.reason?.message || jwtResult.reason}`);
|
||||||
return;
|
return;
|
||||||
@@ -49,7 +36,6 @@ export async function refreshAllKeys(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Atomic swap (синхронные операции, нельзя прервать) ──────────────────
|
|
||||||
swapKeyMap(jwtResult.value);
|
swapKeyMap(jwtResult.value);
|
||||||
if (csrfResult.status === 'fulfilled' && csrfResult.value) {
|
if (csrfResult.status === 'fulfilled' && csrfResult.value) {
|
||||||
swapCsrfConfig(csrfResult.value);
|
swapCsrfConfig(csrfResult.value);
|
||||||
@@ -61,6 +47,7 @@ export async function refreshAllKeys(): Promise<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function startKeyRotation(intervalMs: number = DEFAULT_INTERVAL_MS): void {
|
export function startKeyRotation(intervalMs: number = DEFAULT_INTERVAL_MS): void {
|
||||||
if (timer) return;
|
if (timer) return;
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
@@ -68,12 +55,11 @@ export function startKeyRotation(intervalMs: number = DEFAULT_INTERVAL_MS): void
|
|||||||
void refreshAllKeys().catch((err) =>
|
void refreshAllKeys().catch((err) =>
|
||||||
logger.error(`Key rotation tick failed: ${err?.message || err}`)
|
logger.error(`Key rotation tick failed: ${err?.message || err}`)
|
||||||
);
|
);
|
||||||
// На каждый тик — invalidate Vault token (он мог истечь), будет re-login
|
|
||||||
currentVaultToken = null;
|
|
||||||
}, intervalMs);
|
}, intervalMs);
|
||||||
logger.info(`Key rotation scheduled (every ${intervalMs}ms)`);
|
logger.info(`Key rotation scheduled (every ${intervalMs}ms)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function stopKeyRotation(): void {
|
export function stopKeyRotation(): void {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
|
image: cryptowallet-api:latest
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@@ -9,51 +10,13 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
API_PORT: '${API_PORT:-3001}'
|
API_PORT: '${API_PORT:-3001}'
|
||||||
LOG_LEVEL: '${LOG_LEVEL:-INFO}'
|
LOG_LEVEL: '${LOG_LEVEL:-INFO}'
|
||||||
DB_HOST: '${DB_HOST:-postgres}'
|
VAULT_ADDR: '${VAULT_ADDR:?VAULT_ADDR is required}'
|
||||||
DB_PORT: '${DB_PORT:-5432}'
|
VAULT_ROLE_ID: '${VAULT_ROLE_ID:?VAULT_ROLE_ID is required}'
|
||||||
DB_USER: '${DB_USER:-cryptowallet}'
|
VAULT_SECRET_ID: '${VAULT_SECRET_ID:?VAULT_SECRET_ID is required}'
|
||||||
DB_PASSWORD: '${DB_PASSWORD:-cryptowallet}'
|
|
||||||
DB_NAME: '${DB_NAME:-cryptowallet}'
|
|
||||||
VAULT_ADDR: '${VAULT_ADDR:-}'
|
|
||||||
VAULT_ROLE_ID: '${VAULT_ROLE_ID:-}'
|
|
||||||
VAULT_SECRET_ID: '${VAULT_SECRET_ID:-}'
|
|
||||||
VAULT_MOUNT_POINT: '${VAULT_MOUNT_POINT:-dev-secrets}'
|
VAULT_MOUNT_POINT: '${VAULT_MOUNT_POINT:-dev-secrets}'
|
||||||
VAULT_SECRET_PATH: '${VAULT_SECRET_PATH:-database}'
|
VAULT_SECRET_PATH: '${VAULT_SECRET_PATH:-database}'
|
||||||
VAULT_JWT_KID_PATH: '${VAULT_JWT_KID_PATH:-jwt/kid}'
|
VAULT_JWT_KID_PATH: '${VAULT_JWT_KID_PATH:-jwt/kid}'
|
||||||
VAULT_JWT_KIDS_PREFIX: '${VAULT_JWT_KIDS_PREFIX:-jwt/kids}'
|
VAULT_JWT_KIDS_PREFIX: '${VAULT_JWT_KIDS_PREFIX:-jwt/kids}'
|
||||||
VAULT_CSRF_PATH: '${VAULT_CSRF_PATH:-}'
|
VAULT_CSRF_PATH: '${VAULT_CSRF_PATH:-}'
|
||||||
JWT_ALGORITHM: '${JWT_ALGORITHM:-RS256}'
|
JWT_ALGORITHM: '${JWT_ALGORITHM:-RS256}'
|
||||||
JWT_ISSUER: '${JWT_ISSUER:-auth-service}'
|
|
||||||
JWT_AUDIENCE: '${JWT_AUDIENCE:-elcsa}'
|
|
||||||
CORS_ORIGINS: '${CORS_ORIGINS:-}'
|
|
||||||
CORS_ALLOW_CREDENTIALS: '${CORS_ALLOW_CREDENTIALS:-true}'
|
|
||||||
RELAY_API_KEY: '${RELAY_API_KEY:-}'
|
|
||||||
TRON_API_KEY: '${TRON_API_KEY:-}'
|
|
||||||
JUPITER_API_KEY: '${JUPITER_API_KEY:-}'
|
|
||||||
JUPITER_REFERRAL_ACCOUNT: '${JUPITER_REFERRAL_ACCOUNT:-}'
|
|
||||||
JUPITER_FEE_BPS: '${JUPITER_FEE_BPS:-70}'
|
JUPITER_FEE_BPS: '${JUPITER_FEE_BPS:-70}'
|
||||||
ETHERSCAN_API_KEY: '${ETHERSCAN_API_KEY:-}'
|
|
||||||
BSCSCAN_API_KEY: '${BSCSCAN_API_KEY:-}'
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: '${DB_USER:-cryptowallet}'
|
|
||||||
POSTGRES_PASSWORD: '${DB_PASSWORD:-cryptowallet}'
|
|
||||||
POSTGRES_DB: '${DB_NAME:-cryptowallet}'
|
|
||||||
ports:
|
|
||||||
- '${POSTGRES_PORT:-5432}:5432'
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD-SHELL','pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"']
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data:
|
|
||||||
|
|||||||
Reference in New Issue
Block a user