@lobehub/lobehub 2.0.0-next.161 → 2.0.0-next.163
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/changelog/v1.json +14 -0
- package/locales/ar/authError.json +40 -0
- package/locales/bg-BG/authError.json +40 -0
- package/locales/de-DE/authError.json +40 -0
- package/locales/en-US/authError.json +40 -0
- package/locales/es-ES/authError.json +40 -0
- package/locales/fa-IR/authError.json +40 -0
- package/locales/fr-FR/authError.json +40 -0
- package/locales/it-IT/authError.json +40 -0
- package/locales/ja-JP/authError.json +40 -0
- package/locales/ko-KR/authError.json +40 -0
- package/locales/nl-NL/authError.json +40 -0
- package/locales/pl-PL/authError.json +40 -0
- package/locales/pt-BR/authError.json +40 -0
- package/locales/ru-RU/authError.json +40 -0
- package/locales/tr-TR/authError.json +40 -0
- package/locales/vi-VN/authError.json +40 -0
- package/locales/zh-CN/authError.json +40 -0
- package/locales/zh-CN/setting.json +2 -2
- package/locales/zh-TW/authError.json +40 -0
- package/package.json +1 -1
- package/src/app/[variants]/(auth)/auth-error/page.tsx +59 -0
- package/src/app/[variants]/(main)/discover/(list)/_layout/Mobile/Nav.tsx +13 -1
- package/src/app/[variants]/(main)/discover/(list)/features/Pagination.tsx +6 -2
- package/src/app/[variants]/(main)/discover/components/Title.tsx +22 -2
- package/src/auth.ts +13 -48
- package/src/envs/redis.ts +1 -1
- package/src/libs/better-auth/utils/config.ts +91 -0
- package/src/libs/redis/manager.ts +5 -1
- package/src/libs/redis/redis.test.ts +1 -1
- package/src/libs/redis/upstash.test.ts +9 -5
- package/src/libs/redis/upstash.ts +44 -20
- package/src/locales/default/authError.ts +40 -0
- package/src/locales/default/index.ts +2 -0
- package/src/proxy.ts +1 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// Read more here: https://github.com/capricorn86/happy-dom/issues/1042#issuecomment-3585851354
|
|
7
7
|
import { Buffer } from 'node:buffer';
|
|
8
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
8
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
9
9
|
|
|
10
10
|
import { UpstashConfig } from './types';
|
|
11
11
|
|
|
@@ -139,9 +139,9 @@ describe('mocked', () => {
|
|
|
139
139
|
await provider.hset(bufKey, 'field', 'value');
|
|
140
140
|
await provider.del(bufKey);
|
|
141
141
|
|
|
142
|
-
expect(mocks.mockSet).toHaveBeenCalledWith('buffer-key', 'value', undefined);
|
|
143
|
-
expect(mocks.mockHset).toHaveBeenCalledWith('buffer-key', { field: 'value' });
|
|
144
|
-
expect(mocks.mockDel).toHaveBeenCalledWith('buffer-key');
|
|
142
|
+
expect(mocks.mockSet).toHaveBeenCalledWith('mock:buffer-key', 'value', undefined);
|
|
143
|
+
expect(mocks.mockHset).toHaveBeenCalledWith('mock:buffer-key', { field: 'value' });
|
|
144
|
+
expect(mocks.mockDel).toHaveBeenCalledWith('mock:buffer-key');
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
it('passes set options through to upstash client', async () => {
|
|
@@ -149,6 +149,10 @@ describe('mocked', () => {
|
|
|
149
149
|
|
|
150
150
|
await provider.set('key', 'value', { ex: 10, nx: true, get: true });
|
|
151
151
|
|
|
152
|
-
expect(mocks.mockSet).toHaveBeenCalledWith('key', 'value', {
|
|
152
|
+
expect(mocks.mockSet).toHaveBeenCalledWith('mock:key', 'value', {
|
|
153
|
+
ex: 10,
|
|
154
|
+
nx: true,
|
|
155
|
+
get: true,
|
|
156
|
+
});
|
|
153
157
|
});
|
|
154
158
|
});
|
|
@@ -20,9 +20,28 @@ import {
|
|
|
20
20
|
export class UpstashRedisProvider implements BaseRedisProvider {
|
|
21
21
|
provider: 'upstash' = 'upstash';
|
|
22
22
|
private client: Redis;
|
|
23
|
+
private readonly prefix: string;
|
|
23
24
|
|
|
24
25
|
constructor(options: UpstashConfig | RedisConfigNodejs) {
|
|
25
|
-
|
|
26
|
+
const { prefix, ...clientOptions } = options as UpstashConfig & RedisConfigNodejs;
|
|
27
|
+
this.prefix = prefix ? `${prefix}:` : '';
|
|
28
|
+
this.client = new Redis(clientOptions as RedisConfigNodejs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build a fully qualified key assuming the input was already normalized.
|
|
33
|
+
* Avoids re-running normalization when callers have normalized keys (e.g. mset).
|
|
34
|
+
*/
|
|
35
|
+
private addPrefixToKey(normalizedKey: string) {
|
|
36
|
+
return `${this.prefix}${normalizedKey}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private buildKey(key: RedisKey) {
|
|
40
|
+
return this.addPrefixToKey(normalizeRedisKey(key));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private buildKeys(keys: RedisKey[]) {
|
|
44
|
+
return normalizeRedisKeys(keys).map((key) => `${this.prefix}${key}`);
|
|
26
45
|
}
|
|
27
46
|
|
|
28
47
|
async initialize(): Promise<void> {
|
|
@@ -34,15 +53,11 @@ export class UpstashRedisProvider implements BaseRedisProvider {
|
|
|
34
53
|
}
|
|
35
54
|
|
|
36
55
|
async get(key: RedisKey): Promise<string | null> {
|
|
37
|
-
return this.client.get(
|
|
56
|
+
return this.client.get(this.buildKey(key));
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
async set(key: RedisKey, value: RedisValue, options?: SetOptions): Promise<RedisSetResult> {
|
|
41
|
-
const res = await this.client.set(
|
|
42
|
-
normalizeRedisKey(key),
|
|
43
|
-
value,
|
|
44
|
-
buildUpstashSetOptions(options),
|
|
45
|
-
);
|
|
60
|
+
const res = await this.client.set(this.buildKey(key), value, buildUpstashSetOptions(options));
|
|
46
61
|
if (Buffer.isBuffer(res)) {
|
|
47
62
|
return res.toString();
|
|
48
63
|
}
|
|
@@ -51,55 +66,64 @@ export class UpstashRedisProvider implements BaseRedisProvider {
|
|
|
51
66
|
}
|
|
52
67
|
|
|
53
68
|
async setex(key: RedisKey, seconds: number, value: RedisValue): Promise<'OK'> {
|
|
54
|
-
return this.client.setex(
|
|
69
|
+
return this.client.setex(this.buildKey(key), seconds, value);
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
async del(...keys: RedisKey[]): Promise<number> {
|
|
58
|
-
return this.client.del(...
|
|
73
|
+
return this.client.del(...this.buildKeys(keys));
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
async exists(...keys: RedisKey[]): Promise<number> {
|
|
62
|
-
return this.client.exists(...
|
|
77
|
+
return this.client.exists(...this.buildKeys(keys));
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
async expire(key: RedisKey, seconds: number): Promise<number> {
|
|
66
|
-
return this.client.expire(
|
|
81
|
+
return this.client.expire(this.buildKey(key), seconds);
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
async ttl(key: RedisKey): Promise<number> {
|
|
70
|
-
return this.client.ttl(
|
|
85
|
+
return this.client.ttl(this.buildKey(key));
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
async incr(key: RedisKey): Promise<number> {
|
|
74
|
-
return this.client.incr(
|
|
89
|
+
return this.client.incr(this.buildKey(key));
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
async decr(key: RedisKey): Promise<number> {
|
|
78
|
-
return this.client.decr(
|
|
93
|
+
return this.client.decr(this.buildKey(key));
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
async mget(...keys: RedisKey[]): Promise<(string | null)[]> {
|
|
82
|
-
return this.client.mget(...
|
|
97
|
+
return this.client.mget(...this.buildKeys(keys));
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
async mset(values: RedisMSetArgument): Promise<'OK'> {
|
|
86
|
-
|
|
101
|
+
const normalized = normalizeMsetValues(values);
|
|
102
|
+
const prefixed = Object.entries(normalized).reduce<Record<string, RedisValue>>(
|
|
103
|
+
(acc, [key, value]) => {
|
|
104
|
+
acc[this.addPrefixToKey(key)] = value;
|
|
105
|
+
return acc;
|
|
106
|
+
},
|
|
107
|
+
{},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return this.client.mset(prefixed);
|
|
87
111
|
}
|
|
88
112
|
|
|
89
113
|
async hget(key: RedisKey, field: RedisKey): Promise<string | null> {
|
|
90
|
-
return this.client.hget(
|
|
114
|
+
return this.client.hget(this.buildKey(key), normalizeRedisKey(field));
|
|
91
115
|
}
|
|
92
116
|
|
|
93
117
|
async hset(key: RedisKey, field: RedisKey, value: RedisValue): Promise<number> {
|
|
94
|
-
return this.client.hset(
|
|
118
|
+
return this.client.hset(this.buildKey(key), { [normalizeRedisKey(field)]: value });
|
|
95
119
|
}
|
|
96
120
|
|
|
97
121
|
async hdel(key: RedisKey, ...fields: RedisKey[]): Promise<number> {
|
|
98
|
-
return this.client.hdel(
|
|
122
|
+
return this.client.hdel(this.buildKey(key), ...normalizeRedisKeys(fields));
|
|
99
123
|
}
|
|
100
124
|
|
|
101
125
|
async hgetall(key: RedisKey): Promise<Record<string, string>> {
|
|
102
|
-
const res = await this.client.hgetall(
|
|
126
|
+
const res = await this.client.hgetall(this.buildKey(key));
|
|
103
127
|
if (!res) {
|
|
104
128
|
return {};
|
|
105
129
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
actions: {
|
|
3
|
+
discord: '前往 Discord 反馈',
|
|
4
|
+
home: '返回首页',
|
|
5
|
+
retry: '重新登录',
|
|
6
|
+
},
|
|
7
|
+
codes: {
|
|
8
|
+
ACCOUNT_ALREADY_LINKED_TO_DIFFERENT_USER: '该账号已关联至其他用户',
|
|
9
|
+
ACCOUNT_NOT_FOUND: '未找到对应账号',
|
|
10
|
+
CREDENTIAL_ACCOUNT_NOT_FOUND: '凭证账号不存在',
|
|
11
|
+
EMAIL_CAN_NOT_BE_UPDATED: '当前账号邮箱不可修改',
|
|
12
|
+
EMAIL_NOT_VERIFIED: '请先完成邮箱验证',
|
|
13
|
+
FAILED_TO_CREATE_SESSION: '创建会话失败',
|
|
14
|
+
FAILED_TO_CREATE_USER: '创建用户失败',
|
|
15
|
+
FAILED_TO_GET_SESSION: '获取会话失败',
|
|
16
|
+
FAILED_TO_GET_USER_INFO: '获取用户信息失败',
|
|
17
|
+
FAILED_TO_UNLINK_LAST_ACCOUNT: '无法解绑最后一个关联账号',
|
|
18
|
+
FAILED_TO_UPDATE_USER: '更新用户信息失败',
|
|
19
|
+
ID_TOKEN_NOT_SUPPORTED: '当前身份令牌不被支持',
|
|
20
|
+
INVALID_EMAIL: '邮箱格式不正确',
|
|
21
|
+
INVALID_EMAIL_OR_PASSWORD: '邮箱或密码错误',
|
|
22
|
+
INVALID_PASSWORD: '密码格式无效',
|
|
23
|
+
INVALID_TOKEN: '令牌无效或已过期',
|
|
24
|
+
PASSWORD_TOO_LONG: '密码长度过长',
|
|
25
|
+
PASSWORD_TOO_SHORT: '密码长度过短',
|
|
26
|
+
PROVIDER_NOT_FOUND: '未找到对应的身份提供方配置',
|
|
27
|
+
RATE_LIMIT_EXCEEDED: '请求过于频繁,请稍后再试',
|
|
28
|
+
SESSION_EXPIRED: '会话已过期,请重新登录',
|
|
29
|
+
SOCIAL_ACCOUNT_ALREADY_LINKED: '该社交账号已被其他用户绑定',
|
|
30
|
+
UNEXPECTED_ERROR: '发生未知错误,请重试',
|
|
31
|
+
UNKNOWN: '发生未知错误,请重试或联系支持',
|
|
32
|
+
USER_ALREADY_EXISTS: '用户已存在',
|
|
33
|
+
USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL: '邮箱已被使用,请尝试其他邮箱',
|
|
34
|
+
USER_ALREADY_HAS_PASSWORD: '该账号已设置密码',
|
|
35
|
+
USER_BANNED: '该用户已被封禁',
|
|
36
|
+
USER_EMAIL_NOT_FOUND: '未找到对应邮箱',
|
|
37
|
+
USER_NOT_FOUND: '未找到用户',
|
|
38
|
+
},
|
|
39
|
+
title: '身份验证出错',
|
|
40
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import auth from './auth';
|
|
2
|
+
import authError from './authError';
|
|
2
3
|
import changelog from './changelog';
|
|
3
4
|
import chat from './chat';
|
|
4
5
|
import clerk from './clerk';
|
|
@@ -33,6 +34,7 @@ import welcome from './welcome';
|
|
|
33
34
|
|
|
34
35
|
const resources = {
|
|
35
36
|
auth,
|
|
37
|
+
authError,
|
|
36
38
|
changelog,
|
|
37
39
|
chat,
|
|
38
40
|
clerk,
|