mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-02 09:59:55 +08:00
feat(server): support refresh token (#15218)
This commit is contained in:
@@ -15,6 +15,14 @@ end
|
||||
return value
|
||||
`;
|
||||
|
||||
const INCREASE_WITH_TTL_LUA = `
|
||||
local value = redis.call("INCRBY", KEYS[1], ARGV[1])
|
||||
if value == tonumber(ARGV[1]) or redis.call("PTTL", KEYS[1]) == -1 then
|
||||
redis.call("PEXPIRE", KEYS[1], ARGV[2])
|
||||
end
|
||||
return value
|
||||
`;
|
||||
|
||||
export function isValidCacheTtl(ttl: unknown): ttl is number {
|
||||
return typeof ttl === 'number' && Number.isSafeInteger(ttl) && ttl > 0;
|
||||
}
|
||||
@@ -60,6 +68,18 @@ export class CacheProvider {
|
||||
return this.redis.incrby(key, count).catch(() => 0);
|
||||
}
|
||||
|
||||
async increaseWithTtl(
|
||||
key: string,
|
||||
ttl: number,
|
||||
count: number = 1
|
||||
): Promise<number> {
|
||||
if (!isValidCacheTtl(ttl)) return 0;
|
||||
return this.redis
|
||||
.eval(INCREASE_WITH_TTL_LUA, 1, key, count, ttl)
|
||||
.then(value => Number(value))
|
||||
.catch(() => 0);
|
||||
}
|
||||
|
||||
async decrease(key: string, count: number = 1): Promise<number> {
|
||||
return this.redis.decrby(key, count).catch(() => 0);
|
||||
}
|
||||
|
||||
@@ -29,10 +29,12 @@ export const CORS_ALLOWED_HEADERS = [
|
||||
'authorization',
|
||||
'content-type',
|
||||
'x-affine-version',
|
||||
'x-affine-client-kind',
|
||||
'x-operation-name',
|
||||
'x-request-id',
|
||||
'x-captcha-token',
|
||||
'x-captcha-challenge',
|
||||
'x-captcha-provider',
|
||||
'x-affine-csrf-token',
|
||||
'x-requested-with',
|
||||
'range',
|
||||
|
||||
@@ -422,6 +422,34 @@ export const USER_FRIENDLY_ERRORS = {
|
||||
type: 'authentication_required',
|
||||
message: 'You must sign in first to access this resource.',
|
||||
},
|
||||
access_token_expired: {
|
||||
type: 'authentication_required',
|
||||
message: 'The access token has expired.',
|
||||
},
|
||||
access_token_invalid: {
|
||||
type: 'authentication_required',
|
||||
message: 'The access token is invalid.',
|
||||
},
|
||||
auth_session_expired: {
|
||||
type: 'authentication_required',
|
||||
message: 'The auth session has expired.',
|
||||
},
|
||||
auth_session_revoked: {
|
||||
type: 'authentication_required',
|
||||
message: 'The auth session has been revoked.',
|
||||
},
|
||||
refresh_token_invalid: {
|
||||
type: 'authentication_required',
|
||||
message: 'The refresh token is invalid.',
|
||||
},
|
||||
refresh_token_reused: {
|
||||
type: 'authentication_required',
|
||||
message: 'The refresh token has already been used.',
|
||||
},
|
||||
auth_session_temporarily_unavailable: {
|
||||
type: 'network_error',
|
||||
message: 'Auth session service is temporarily unavailable.',
|
||||
},
|
||||
action_forbidden: {
|
||||
type: 'action_forbidden',
|
||||
message: 'You are not allowed to perform this action.',
|
||||
|
||||
@@ -274,6 +274,48 @@ export class AuthenticationRequired extends UserFriendlyError {
|
||||
}
|
||||
}
|
||||
|
||||
export class AccessTokenExpired extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('authentication_required', 'access_token_expired', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class AccessTokenInvalid extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('authentication_required', 'access_token_invalid', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthSessionExpired extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('authentication_required', 'auth_session_expired', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthSessionRevoked extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('authentication_required', 'auth_session_revoked', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class RefreshTokenInvalid extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('authentication_required', 'refresh_token_invalid', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class RefreshTokenReused extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('authentication_required', 'refresh_token_reused', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthSessionTemporarilyUnavailable extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('network_error', 'auth_session_temporarily_unavailable', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class ActionForbidden extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('action_forbidden', 'action_forbidden', message);
|
||||
@@ -1200,6 +1242,13 @@ export enum ErrorNames {
|
||||
INVALID_EMAIL_TOKEN,
|
||||
LINK_EXPIRED,
|
||||
AUTHENTICATION_REQUIRED,
|
||||
ACCESS_TOKEN_EXPIRED,
|
||||
ACCESS_TOKEN_INVALID,
|
||||
AUTH_SESSION_EXPIRED,
|
||||
AUTH_SESSION_REVOKED,
|
||||
REFRESH_TOKEN_INVALID,
|
||||
REFRESH_TOKEN_REUSED,
|
||||
AUTH_SESSION_TEMPORARILY_UNAVAILABLE,
|
||||
ACTION_FORBIDDEN,
|
||||
ACCESS_DENIED,
|
||||
EMAIL_VERIFICATION_REQUIRED,
|
||||
|
||||
Reference in New Issue
Block a user