ok
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
PORT=3001
|
||||
HOST=0.0.0.0
|
||||
TRUE_API_URL=https://markirovka.sandbox.crptech.ru/api/v3/true-api
|
||||
SANDBOX_URL=https://markirovka.sandbox.crptech.ru/api/v3/true-api
|
||||
PROD_URL=https://markirovka.crpt.ru/api/v3/true-api
|
||||
PUBLIC_CHECK_URL=https://mobile.api.crpt.ru/mobile/check
|
||||
CORS_ORIGIN=*
|
||||
|
||||
+13
-2
@@ -1,3 +1,16 @@
|
||||
export type TrueApiEnv = 'sandbox' | 'prod'
|
||||
|
||||
export const envUrls: Record<TrueApiEnv, { baseUrl: string; publicCheckUrl: string }> = {
|
||||
sandbox: {
|
||||
baseUrl: process.env.SANDBOX_URL || 'https://markirovka.sandbox.crptech.ru/api/v3/true-api',
|
||||
publicCheckUrl: process.env.SANDBOX_PUBLIC_CHECK_URL || 'https://mobile.api.crpt.ru/mobile/check',
|
||||
},
|
||||
prod: {
|
||||
baseUrl: process.env.PROD_URL || 'https://markirovka.crpt.ru/api/v3/true-api',
|
||||
publicCheckUrl: process.env.PROD_PUBLIC_CHECK_URL || 'https://mobile.api.crpt.ru/mobile/check',
|
||||
},
|
||||
}
|
||||
|
||||
export const config = {
|
||||
port: parseInt(process.env.PORT || '3001', 10),
|
||||
host: process.env.HOST || '0.0.0.0',
|
||||
@@ -5,8 +18,6 @@ export const config = {
|
||||
corsOrigin: process.env.CORS_ORIGIN || '*',
|
||||
|
||||
trueApi: {
|
||||
baseUrl: process.env.TRUE_API_URL || 'https://markirovka.sandbox.crptech.ru/api/v3/true-api',
|
||||
publicCheckUrl: process.env.PUBLIC_CHECK_URL || 'https://mobile.api.crpt.ru/mobile/check',
|
||||
authPath: '/auth',
|
||||
cisesInfoPath: '/cises/info',
|
||||
codesCheckPath: '/codes/check',
|
||||
|
||||
@@ -5,6 +5,7 @@ import { config } from './config.js'
|
||||
import { authRoutes } from './routes/auth.js'
|
||||
import { cisesRoutes } from './routes/cises.js'
|
||||
import { updRoutes } from './routes/upd.js'
|
||||
import { plansRoutes } from './routes/plans.js'
|
||||
|
||||
const app = Fastify({ logger: true })
|
||||
|
||||
@@ -20,6 +21,7 @@ await app.register(multipart, {
|
||||
await app.register(authRoutes)
|
||||
await app.register(cisesRoutes)
|
||||
await app.register(updRoutes)
|
||||
await app.register(plansRoutes)
|
||||
|
||||
app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }))
|
||||
|
||||
|
||||
+57
-21
@@ -1,6 +1,15 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { getAuthKey, signIn, clearToken, isAuthenticated } from '../services/auth-service.js'
|
||||
import { getTokenTTL } from '../services/token-cache.js'
|
||||
import type { TrueApiEnv } from '../config.js'
|
||||
import { getAuthKey, signIn, clearToken, getAuthStatus } from '../services/auth-service.js'
|
||||
import { AppError, extractDebugInfo } from '../services/cises-service.js'
|
||||
|
||||
const VALID_ENVS = ['sandbox', 'prod']
|
||||
|
||||
function parseEnv(query: Record<string, string>): TrueApiEnv {
|
||||
const env = query.env
|
||||
if (env && VALID_ENVS.includes(env)) return env as TrueApiEnv
|
||||
return 'sandbox'
|
||||
}
|
||||
|
||||
const signInSchema = {
|
||||
body: {
|
||||
@@ -14,28 +23,55 @@ const signInSchema = {
|
||||
}
|
||||
|
||||
export async function authRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.get('/auth/key', async () => {
|
||||
const authKey = await getAuthKey()
|
||||
return { uuid: authKey.uuid, data: authKey.data }
|
||||
})
|
||||
|
||||
app.post<{ Body: { uuid: string; data: string } }>('/auth/simpleSignIn', { schema: signInSchema }, async (request) => {
|
||||
const { uuid, data } = request.body
|
||||
const result = await signIn({ uuid, data })
|
||||
return result
|
||||
})
|
||||
|
||||
app.get('/auth/status', async () => {
|
||||
const authenticated = isAuthenticated()
|
||||
const ttl = getTokenTTL()
|
||||
return {
|
||||
authenticated,
|
||||
tokenExpiresAt: ttl ? new Date(ttl).toISOString() : null,
|
||||
app.get('/auth/key', async (request, reply) => {
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
try {
|
||||
const authKey = await getAuthKey(env)
|
||||
return { uuid: authKey.uuid, data: authKey.data }
|
||||
} catch (err) {
|
||||
if (err instanceof AppError) {
|
||||
return reply.status(err.statusCode).send({
|
||||
error: err.message,
|
||||
debugInfo: err.debugInfo,
|
||||
})
|
||||
}
|
||||
const debugInfo = extractDebugInfo(err)
|
||||
return reply.status(502).send({
|
||||
error: 'Failed to get auth key from True API',
|
||||
debugInfo,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/auth/logout', async () => {
|
||||
clearToken()
|
||||
app.post<{ Body: { uuid: string; data: string } }>('/auth/simpleSignIn', { schema: signInSchema }, async (request, reply) => {
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
try {
|
||||
const { uuid, data } = request.body
|
||||
const result = await signIn(env, { uuid, data })
|
||||
return result
|
||||
} catch (err) {
|
||||
if (err instanceof AppError) {
|
||||
return reply.status(err.statusCode).send({
|
||||
error: err.message,
|
||||
debugInfo: err.debugInfo,
|
||||
})
|
||||
}
|
||||
const debugInfo = extractDebugInfo(err)
|
||||
return reply.status(502).send({
|
||||
error: 'Failed to sign in to True API',
|
||||
debugInfo,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/auth/status', async (request) => {
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
return getAuthStatus(env)
|
||||
})
|
||||
|
||||
app.post('/auth/logout', async (request) => {
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
clearToken(env)
|
||||
return { success: true }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { TrueApiEnv } from '../config.js'
|
||||
import { checkCodesPublic, checkCodesAuth, AppError } from '../services/cises-service.js'
|
||||
import type { CheckCodesRequest } from '../types/index.js'
|
||||
|
||||
const VALID_ENVS = ['sandbox', 'prod']
|
||||
|
||||
function parseEnv(query: Record<string, string>): TrueApiEnv {
|
||||
const env = query.env
|
||||
if (env && VALID_ENVS.includes(env)) return env as TrueApiEnv
|
||||
return 'sandbox'
|
||||
}
|
||||
|
||||
const checkCodesSchema = {
|
||||
body: {
|
||||
type: 'object',
|
||||
@@ -19,15 +28,20 @@ const checkCodesSchema = {
|
||||
|
||||
export async function cisesRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.post<{ Body: CheckCodesRequest }>('/api/check-codes/public', { schema: checkCodesSchema }, async (request) => {
|
||||
return checkCodesPublic(request.body)
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
return checkCodesPublic(env, request.body)
|
||||
})
|
||||
|
||||
app.post<{ Body: CheckCodesRequest }>('/api/check-codes/auth', { schema: checkCodesSchema }, async (request, reply) => {
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
try {
|
||||
return await checkCodesAuth(request.body)
|
||||
return await checkCodesAuth(env, request.body)
|
||||
} catch (err) {
|
||||
if (err instanceof AppError) {
|
||||
return reply.status(err.statusCode).send({ error: err.message })
|
||||
return reply.status(err.statusCode).send({
|
||||
error: err.message,
|
||||
debugInfo: err.debugInfo,
|
||||
})
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { readdir, readFile, stat } from 'node:fs/promises'
|
||||
import { resolve, extname } from 'node:path'
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
|
||||
const PLANS_DIR = resolve(import.meta.dirname || __dirname, '../../plans')
|
||||
|
||||
export async function plansRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.get('/api/plans', async () => {
|
||||
const files = await readdir(PLANS_DIR)
|
||||
const mdFiles: { name: string; path: string; size: number }[] = []
|
||||
|
||||
for (const file of files) {
|
||||
if (extname(file) !== '.md') continue
|
||||
const fullPath = resolve(PLANS_DIR, file)
|
||||
const stats = await stat(fullPath)
|
||||
mdFiles.push({ name: file, path: file, size: stats.size })
|
||||
}
|
||||
|
||||
mdFiles.sort((a, b) => b.name.localeCompare(a.name))
|
||||
return mdFiles
|
||||
})
|
||||
|
||||
app.get<{ Params: { filename: string } }>('/api/plans/:filename', async (request, reply) => {
|
||||
const { filename } = request.params
|
||||
|
||||
if (extname(filename) !== '.md' || filename.includes('..') || filename.includes('/')) {
|
||||
return reply.status(400).send({ error: 'Invalid filename' })
|
||||
}
|
||||
|
||||
const fullPath = resolve(PLANS_DIR, filename)
|
||||
|
||||
try {
|
||||
await stat(fullPath)
|
||||
} catch {
|
||||
return reply.status(404).send({ error: 'File not found' })
|
||||
}
|
||||
|
||||
const content = await readFile(fullPath, 'utf-8')
|
||||
return { name: filename, content }
|
||||
})
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { TrueApiEnv } from '../config.js'
|
||||
import { parseUpdXml } from '../services/upd-parser.js'
|
||||
import { checkCodesAuth, AppError } from '../services/cises-service.js'
|
||||
|
||||
const VALID_ENVS = ['sandbox', 'prod']
|
||||
|
||||
function parseEnv(query: Record<string, string>): TrueApiEnv {
|
||||
const env = query.env
|
||||
if (env && VALID_ENVS.includes(env)) return env as TrueApiEnv
|
||||
return 'sandbox'
|
||||
}
|
||||
|
||||
export async function updRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.post('/api/upload-upd', async (request, reply) => {
|
||||
const env = parseEnv(request.query as Record<string, string>)
|
||||
const data = await request.file()
|
||||
if (!data) {
|
||||
return reply.status(400).send({ error: 'No file uploaded' })
|
||||
@@ -25,7 +35,7 @@ export async function updRoutes(app: FastifyInstance): Promise<void> {
|
||||
}
|
||||
|
||||
try {
|
||||
const checkResults = await checkCodesAuth({ codes: parsed.codes })
|
||||
const checkResults = await checkCodesAuth(env, { codes: parsed.codes })
|
||||
|
||||
return {
|
||||
document: {
|
||||
@@ -40,7 +50,10 @@ export async function updRoutes(app: FastifyInstance): Promise<void> {
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AppError) {
|
||||
return reply.status(err.statusCode).send({ error: err.message })
|
||||
return reply.status(err.statusCode).send({
|
||||
error: err.message,
|
||||
debugInfo: err.debugInfo,
|
||||
})
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,34 +1,58 @@
|
||||
import got from 'got'
|
||||
import { config } from '../config.js'
|
||||
import { getToken, setToken, invalidateToken } from './token-cache.js'
|
||||
import { config, envUrls } from '../config.js'
|
||||
import type { TrueApiEnv } from '../config.js'
|
||||
import { getToken, setToken, invalidateToken, hasToken, getTokenTTL } from './token-cache.js'
|
||||
import { AppError, extractDebugInfo } from './cises-service.js'
|
||||
import type { AuthKeyResponse, AuthSignInRequest, AuthSignInResponse } from '../types/index.js'
|
||||
|
||||
const authClient = got.extend({
|
||||
prefixUrl: config.trueApi.baseUrl + config.trueApi.authPath,
|
||||
responseType: 'json',
|
||||
})
|
||||
|
||||
export async function getAuthKey(): Promise<AuthKeyResponse> {
|
||||
const { body } = await authClient.get<AuthKeyResponse>('key')
|
||||
return body
|
||||
}
|
||||
|
||||
export async function signIn(body: AuthSignInRequest): Promise<AuthSignInResponse> {
|
||||
const { body: response } = await authClient.post<AuthSignInResponse>('simpleSignIn', {
|
||||
json: body,
|
||||
function authClient(env: TrueApiEnv) {
|
||||
return got.extend({
|
||||
prefixUrl: envUrls[env].baseUrl + config.trueApi.authPath,
|
||||
responseType: 'json',
|
||||
})
|
||||
setToken(response.token)
|
||||
return response
|
||||
}
|
||||
|
||||
export function getCachedToken(): string | undefined {
|
||||
return getToken()
|
||||
export async function getAuthKey(env: TrueApiEnv): Promise<AuthKeyResponse> {
|
||||
try {
|
||||
const { body } = await authClient(env).get<AuthKeyResponse>('key')
|
||||
return body
|
||||
} catch (err) {
|
||||
const info = extractDebugInfo(err)
|
||||
const status = info?.response.statusCode || 502
|
||||
throw new AppError(info?.response.body ? String(info.response.body) : 'Failed to get auth key', status, info)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearToken(): void {
|
||||
invalidateToken()
|
||||
export async function signIn(env: TrueApiEnv, body: AuthSignInRequest): Promise<AuthSignInResponse> {
|
||||
try {
|
||||
const { body: response } = await authClient(env).post<AuthSignInResponse>('simpleSignIn', {
|
||||
json: body,
|
||||
})
|
||||
setToken(env, response.token)
|
||||
return response
|
||||
} catch (err) {
|
||||
const info = extractDebugInfo(err)
|
||||
const status = info?.response.statusCode || 502
|
||||
throw new AppError(info?.response.body ? String(info.response.body) : 'Failed to sign in', status, info)
|
||||
}
|
||||
}
|
||||
|
||||
export function isAuthenticated(): boolean {
|
||||
return !!getToken()
|
||||
export function getCachedToken(env: TrueApiEnv): string | undefined {
|
||||
return getToken(env)
|
||||
}
|
||||
|
||||
export function clearToken(env: TrueApiEnv): void {
|
||||
invalidateToken(env)
|
||||
}
|
||||
|
||||
export function isAuthenticated(env: TrueApiEnv): boolean {
|
||||
return hasToken(env)
|
||||
}
|
||||
|
||||
export function getAuthStatus(env: TrueApiEnv): { authenticated: boolean; tokenExpiresAt: string | null } {
|
||||
const ttl = getTokenTTL(env)
|
||||
return {
|
||||
authenticated: hasToken(env),
|
||||
tokenExpiresAt: ttl ? new Date(ttl).toISOString() : null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import got from 'got'
|
||||
import { config } from '../config.js'
|
||||
import { config, envUrls } from '../config.js'
|
||||
import type { TrueApiEnv } from '../config.js'
|
||||
import { getCachedToken, clearToken } from './auth-service.js'
|
||||
import type {
|
||||
CisInfoResponse,
|
||||
@@ -9,12 +10,14 @@ import type {
|
||||
SingleCodeResult,
|
||||
} from '../types/index.js'
|
||||
|
||||
const apiClient = got.extend({
|
||||
prefixUrl: config.trueApi.baseUrl,
|
||||
responseType: 'json',
|
||||
})
|
||||
function apiClient(env: TrueApiEnv) {
|
||||
return got.extend({
|
||||
prefixUrl: envUrls[env].baseUrl,
|
||||
responseType: 'json',
|
||||
})
|
||||
}
|
||||
|
||||
export async function checkCodesPublic(body: CheckCodesRequest): Promise<CheckCodesResponse> {
|
||||
export async function checkCodesPublic(env: TrueApiEnv, body: CheckCodesRequest): Promise<CheckCodesResponse> {
|
||||
const results: SingleCodeResult[] = []
|
||||
const queue = [...body.codes]
|
||||
|
||||
@@ -23,7 +26,7 @@ export async function checkCodesPublic(body: CheckCodesRequest): Promise<CheckCo
|
||||
const code = queue.shift()!
|
||||
try {
|
||||
const { body: response } = await got.get<PublicCheckResponse>(
|
||||
`${config.trueApi.publicCheckUrl}?code=${encodeURIComponent(code)}`,
|
||||
`${envUrls[env].publicCheckUrl}?code=${encodeURIComponent(code)}`,
|
||||
{ responseType: 'json', timeout: { request: 10000 } }
|
||||
)
|
||||
results.push({
|
||||
@@ -50,21 +53,21 @@ export async function checkCodesPublic(body: CheckCodesRequest): Promise<CheckCo
|
||||
return buildResponse(results)
|
||||
}
|
||||
|
||||
function getToken(): string {
|
||||
const token = getCachedToken()
|
||||
function getToken(env: TrueApiEnv): string {
|
||||
const token = getCachedToken(env)
|
||||
if (!token) throw new AppError('Not authenticated', 401)
|
||||
return token
|
||||
}
|
||||
|
||||
export async function checkCodesAuth(body: CheckCodesRequest): Promise<CheckCodesResponse> {
|
||||
const token = getToken()
|
||||
export async function checkCodesAuth(env: TrueApiEnv, body: CheckCodesRequest): Promise<CheckCodesResponse> {
|
||||
const token = getToken(env)
|
||||
const results: SingleCodeResult[] = []
|
||||
|
||||
try {
|
||||
await processBatch(body.codes, token, results, 0)
|
||||
await processBatch(env, body.codes, token, results, 0)
|
||||
} catch (err) {
|
||||
if (err instanceof AppError && err.statusCode === 401) {
|
||||
clearToken()
|
||||
clearToken(env)
|
||||
throw err
|
||||
}
|
||||
for (const code of body.codes) {
|
||||
@@ -77,8 +80,58 @@ export async function checkCodesAuth(body: CheckCodesRequest): Promise<CheckCode
|
||||
return buildResponse(results)
|
||||
}
|
||||
|
||||
export interface ApiDebugInfo {
|
||||
request: {
|
||||
method: string
|
||||
url: string
|
||||
headers: Record<string, string>
|
||||
body: unknown
|
||||
}
|
||||
response: {
|
||||
statusCode: number
|
||||
headers: Record<string, string>
|
||||
body: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export function extractDebugInfo(err: unknown): ApiDebugInfo | undefined {
|
||||
if (!err || typeof err !== 'object') return undefined
|
||||
const gotErr = err as Record<string, unknown>
|
||||
const response = gotErr.response as Record<string, unknown> | undefined
|
||||
const options = gotErr.options as Record<string, unknown> | undefined
|
||||
if (!response) return undefined
|
||||
|
||||
const reqHeaders = options?.headers as Record<string, string> | undefined
|
||||
const cleanHeaders: Record<string, string> = {}
|
||||
if (reqHeaders) {
|
||||
for (const [k, v] of Object.entries(reqHeaders)) {
|
||||
if (k.toLowerCase() === 'authorization') {
|
||||
cleanHeaders[k] = 'Bearer ***'
|
||||
} else {
|
||||
cleanHeaders[k] = String(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const resHeaders = response.headers as Record<string, string> | undefined
|
||||
|
||||
return {
|
||||
request: {
|
||||
method: String(options?.method || 'POST'),
|
||||
url: String(response.requestUrl || (options?.url?.toString()) || 'unknown'),
|
||||
headers: cleanHeaders,
|
||||
body: options?.json || options?.body || null,
|
||||
},
|
||||
response: {
|
||||
statusCode: Number(response.statusCode),
|
||||
headers: resHeaders || {},
|
||||
body: response.body || null,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function processBatch(
|
||||
codes: string[], token: string, results: SingleCodeResult[], batchIndex: number
|
||||
env: TrueApiEnv, codes: string[], token: string, results: SingleCodeResult[], batchIndex: number
|
||||
): Promise<void> {
|
||||
if (batchIndex >= codes.length) return
|
||||
|
||||
@@ -86,16 +139,16 @@ async function processBatch(
|
||||
const end = Math.min(start + config.trueApi.batchSize, codes.length)
|
||||
const batch = codes.slice(start, end)
|
||||
|
||||
await retryOnError(batch, token, results)
|
||||
await retryOnError(env, batch, token, results)
|
||||
|
||||
await processBatch(codes, token, results, end)
|
||||
await processBatch(env, codes, token, results, end)
|
||||
}
|
||||
|
||||
async function retryOnError(
|
||||
batch: string[], token: string, results: SingleCodeResult[], attempt = 0
|
||||
env: TrueApiEnv, batch: string[], token: string, results: SingleCodeResult[], attempt = 0
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { body: response } = await apiClient.post<CisInfoResponse>(
|
||||
const { body: response } = await apiClient(env).post<CisInfoResponse>(
|
||||
config.trueApi.cisesInfoPath,
|
||||
{
|
||||
json: { cisList: batch },
|
||||
@@ -118,14 +171,15 @@ async function retryOnError(
|
||||
}
|
||||
} catch (err) {
|
||||
if (is401(err)) {
|
||||
throw new AppError('Token expired', 401)
|
||||
const debugInfo = extractDebugInfo(err)
|
||||
throw new AppError('Token expired', 401, debugInfo)
|
||||
}
|
||||
|
||||
const isRetryable = isRetryableError(err)
|
||||
if (isRetryable && attempt < config.trueApi.maxRetries) {
|
||||
const delay = Math.pow(2, attempt) * 500
|
||||
await sleep(delay)
|
||||
return retryOnError(batch, token, results, attempt + 1)
|
||||
return retryOnError(env, batch, token, results, attempt + 1)
|
||||
}
|
||||
|
||||
throw err
|
||||
@@ -151,9 +205,11 @@ function sleep(ms: number): Promise<void> {
|
||||
|
||||
export class AppError extends Error {
|
||||
statusCode: number
|
||||
constructor(message: string, statusCode: number) {
|
||||
debugInfo?: ApiDebugInfo
|
||||
constructor(message: string, statusCode: number, debugInfo?: ApiDebugInfo) {
|
||||
super(message)
|
||||
this.statusCode = statusCode
|
||||
this.debugInfo = debugInfo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
import NodeCache from 'node-cache'
|
||||
import { config } from '../config.js'
|
||||
import type { TrueApiEnv } from '../config.js'
|
||||
|
||||
const cache = new NodeCache({
|
||||
stdTTL: config.trueApi.tokenTTL / 1000,
|
||||
checkperiod: 600,
|
||||
})
|
||||
|
||||
const TOKEN_KEY = 'true_api_token'
|
||||
|
||||
export function getToken(): string | undefined {
|
||||
return cache.get<string>(TOKEN_KEY)
|
||||
function tokenKey(env: TrueApiEnv): string {
|
||||
return `true_api_token_${env}`
|
||||
}
|
||||
|
||||
export function setToken(token: string): void {
|
||||
cache.set(TOKEN_KEY, token)
|
||||
export function getToken(env: TrueApiEnv): string | undefined {
|
||||
return cache.get<string>(tokenKey(env))
|
||||
}
|
||||
|
||||
export function hasToken(): boolean {
|
||||
return cache.has(TOKEN_KEY)
|
||||
export function setToken(env: TrueApiEnv, token: string): void {
|
||||
cache.set(tokenKey(env), token)
|
||||
}
|
||||
|
||||
export function getTokenTTL(): number | undefined {
|
||||
return cache.getTtl(TOKEN_KEY)
|
||||
export function hasToken(env: TrueApiEnv): boolean {
|
||||
return cache.has(tokenKey(env))
|
||||
}
|
||||
|
||||
export function invalidateToken(): void {
|
||||
cache.del(TOKEN_KEY)
|
||||
export function getTokenTTL(env: TrueApiEnv): number | undefined {
|
||||
return cache.getTtl(tokenKey(env))
|
||||
}
|
||||
|
||||
export function invalidateToken(env: TrueApiEnv): void {
|
||||
cache.del(tokenKey(env))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user