Merge branch 'main' of http://113.45.67.59:3003/insiinc/nice
This commit is contained in:
commit
54c8fa6bf2
|
@ -0,0 +1,21 @@
|
|||
TIMEZONE=Asia/Singapore
|
||||
|
||||
# PostgreSQL 配置
|
||||
POSTGRES_VERSION=latest
|
||||
|
||||
# PostgreSQL Env
|
||||
POSTGRES_DB=nice
|
||||
POSTGRES_USER=nice
|
||||
POSTGRES_PASSWORD=nice
|
||||
|
||||
# Redis 配置
|
||||
REDIS_VERSION=7.2.4
|
||||
REDIS_PASSWORD=nice
|
||||
|
||||
# MinIO 配置
|
||||
MINIO_VERSION=latest
|
||||
MINIO_ACCESS_KEY=nice
|
||||
MINIO_SECRET_KEY=nice123
|
||||
# Elasticsearch 配置
|
||||
ELASTIC_VERSION=9.0.1
|
||||
ELASTIC_PASSWORD=nice_elastic_password
|
|
@ -36,4 +36,5 @@ npm-debug.log*
|
|||
*.pem
|
||||
|
||||
|
||||
packages/db/generated
|
||||
packages/db/generated
|
||||
volumes
|
|
@ -12,6 +12,7 @@
|
|||
"@types/oidc-provider": "^9.1.0",
|
||||
"hono": "^4.7.10",
|
||||
"ioredis": "5.4.1",
|
||||
"jose": "^6.0.11",
|
||||
"minio": "7.1.3",
|
||||
"nanoid": "^5.1.5",
|
||||
"node-cron": "^4.0.7",
|
||||
|
|
|
@ -44,6 +44,7 @@ app.get('/', (c) => {
|
|||
app.use('/oidc/*', async (c, next) => {
|
||||
// @ts-ignore
|
||||
await oidc.callback(c.req.raw, c.res.raw)
|
||||
return c.finalize()
|
||||
// return void 也可以
|
||||
return
|
||||
})
|
||||
export default app
|
||||
|
|
|
@ -1,37 +1,108 @@
|
|||
import { Configuration } from 'oidc-provider';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { RedisAdapter } from './redis-adapter';
|
||||
import { prisma } from '@repo/db';
|
||||
|
||||
async function getClients() {
|
||||
const dbClients = await prisma.oidcClient.findMany?.();
|
||||
const dbClientList = (dbClients && dbClients.length > 0)
|
||||
? dbClients.map(c => ({
|
||||
client_id: c.clientId,
|
||||
client_secret: c.clientSecret,
|
||||
grant_types: JSON.parse(c.grantTypes), // string -> string[]
|
||||
redirect_uris: JSON.parse(c.redirectUris), // string -> string[]
|
||||
response_types: JSON.parse(c.responseTypes), // string -> string[]
|
||||
scope: c.scope,
|
||||
}))
|
||||
: [];
|
||||
|
||||
// 管理后台client,通过环境变量读取
|
||||
const defaultClient = {
|
||||
client_id: process.env.OIDC_CLIENT_ID || 'admin-client',
|
||||
client_secret: process.env.OIDC_CLIENT_SECRET || 'admin-secret',
|
||||
grant_types: ['authorization_code', 'refresh_token'],
|
||||
redirect_uris: [process.env.OIDC_REDIRECT_URI || 'http://localhost:3000/callback'],
|
||||
response_types: ['code'],
|
||||
scope: 'openid email profile',
|
||||
};
|
||||
|
||||
// 检查是否与数据库client_id重复
|
||||
const allClients = [defaultClient, ...dbClientList.filter(c => c.client_id !== defaultClient.client_id)];
|
||||
|
||||
return allClients;
|
||||
}
|
||||
|
||||
const OIDC_COOKIE_KEY = process.env.OIDC_COOKIE_KEY || 'HrbEPlzByV0CcjFJhl2pjKV2iG8FgQIc';
|
||||
|
||||
const config: Configuration = {
|
||||
clients: [
|
||||
{
|
||||
client_id: 'example-client',
|
||||
client_secret: 'example-secret',
|
||||
grant_types: ['authorization_code', 'refresh_token'],
|
||||
redirect_uris: ['http://localhost:3000/callback'],
|
||||
response_types: ['code'],
|
||||
scope: 'openid email profile',
|
||||
},
|
||||
],
|
||||
adapter: RedisAdapter,
|
||||
// 注意:clients字段现在是Promise,需在Provider初始化时await
|
||||
clients: await getClients(),
|
||||
pkce: {
|
||||
required: () => true, // 要求所有客户端使用PKCE
|
||||
required: () => true,
|
||||
},
|
||||
features: {
|
||||
devInteractions: { enabled: false }, // 禁用开发交互界面
|
||||
resourceIndicators: { enabled: true }, // 启用资源指示器
|
||||
revocation: { enabled: true }, // 启用令牌撤销
|
||||
userinfo: { enabled: true }, // 启用用户信息端点
|
||||
devInteractions: { enabled: false },
|
||||
resourceIndicators: { enabled: true },
|
||||
revocation: { enabled: true },
|
||||
userinfo: { enabled: true },
|
||||
registration: { enabled: true },
|
||||
},
|
||||
cookies: {
|
||||
keys: [nanoid()], // 用于签署和验证cookie
|
||||
keys: [OIDC_COOKIE_KEY],
|
||||
},
|
||||
jwks: {
|
||||
keys: [], // 在实际环境中应该生成并保存密钥
|
||||
keys: [
|
||||
{
|
||||
d: 'VEZOsY07JTFzGTqv6cC2Y32vsfChind2I_TTuvV225_-0zrSej3XLRg8iE_u0-3GSgiGi4WImmTwmEgLo4Qp3uEcxCYbt4NMJC7fwT2i3dfRZjtZ4yJwFl0SIj8TgfQ8ptwZbFZUlcHGXZIr4nL8GXyQT0CK8wy4COfmymHrrUoyfZA154ql_OsoiupSUCRcKVvZj2JHL2KILsq_sh_l7g2dqAN8D7jYfJ58MkqlknBMa2-zi5I0-1JUOwztVNml_zGrp27UbEU60RqV3GHjoqwI6m01U7K0a8Q_SQAKYGqgepbAYOA-P4_TLl5KC4-WWBZu_rVfwgSENwWNEhw8oQ',
|
||||
dp: 'E1Y-SN4bQqX7kP-bNgZ_gEv-pixJ5F_EGocHKfS56jtzRqQdTurrk4jIVpI-ZITA88lWAHxjD-OaoJUh9Jupd_lwD5Si80PyVxOMI2xaGQiF0lbKJfD38Sh8frRpgelZVaK_gm834B6SLfxKdNsP04DsJqGKktODF_fZeaGFPH0',
|
||||
dq: 'F90JPxevQYOlAgEH0TUt1-3_hyxY6cfPRU2HQBaahyWrtCWpaOzenKZnvGFZdg-BuLVKjCchq3G_70OLE-XDP_ol0UTJmDTT-WyuJQdEMpt_WFF9yJGoeIu8yohfeLatU-67ukjghJ0s9CBzNE_LrGEV6Cup3FXywpSYZAV3iqc',
|
||||
e: 'AQAB',
|
||||
kty: 'RSA',
|
||||
n: 'xwQ72P9z9OYshiQ-ntDYaPnnfwG6u9JAdLMZ5o0dmjlcyrvwQRdoFIKPnO65Q8mh6F_LDSxjxa2Yzo_wdjhbPZLjfUJXgCzm54cClXzT5twzo7lzoAfaJlkTsoZc2HFWqmcri0BuzmTFLZx2Q7wYBm0pXHmQKF0V-C1O6NWfd4mfBhbM-I1tHYSpAMgarSm22WDMDx-WWI7TEzy2QhaBVaENW9BKaKkJklocAZCxk18WhR0fckIGiWiSM5FcU1PY2jfGsTmX505Ub7P5Dz75Ygqrutd5tFrcqyPAtPTFDk8X1InxkkUwpP3nFU5o50DGhwQolGYKPGtQ-ZtmbOfcWQ',
|
||||
p: '5wC6nY6Ev5FqcLPCqn9fC6R9KUuBej6NaAVOKW7GXiOJAq2WrileGKfMc9kIny20zW3uWkRLm-O-3Yzze1zFpxmqvsvCxZ5ERVZ6leiNXSu3tez71ZZwp0O9gys4knjrI-9w46l_vFuRtjL6XEeFfHEZFaNJpz-lcnb3w0okrbM',
|
||||
q: '3I1qeEDslZFB8iNfpKAdWtz_Wzm6-jayT_V6aIvhvMj5mnU-Xpj75zLPQSGa9wunMlOoZW9w1wDO1FVuDhwzeOJaTm-Ds0MezeC4U6nVGyyDHb4CUA3ml2tzt4yLrqGYMT7XbADSvuWYADHw79OFjEi4T3s3tJymhaBvy1ulv8M',
|
||||
qi: 'wSbXte9PcPtr788e713KHQ4waE26CzoXx-JNOgN0iqJMN6C4_XJEX-cSvCZDf4rh7xpXN6SGLVd5ibIyDJi7bbi5EQ5AXjazPbLBjRthcGXsIuZ3AtQyR0CEWNSdM7EyM5TRdyZQ9kftfz9nI03guW3iKKASETqX2vh0Z8XRjyU',
|
||||
use: 'sig',
|
||||
}, {
|
||||
crv: 'P-256',
|
||||
d: 'K9xfPv773dZR22TVUB80xouzdF7qCg5cWjPjkHyv7Ws',
|
||||
kty: 'EC',
|
||||
use: 'sig',
|
||||
x: 'FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4',
|
||||
y: '_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4',
|
||||
},
|
||||
],
|
||||
},
|
||||
ttl: {
|
||||
AccessToken: 3600, // 1小时
|
||||
AuthorizationCode: 600, // 10分钟
|
||||
IdToken: 3600, // 1小时
|
||||
RefreshToken: 1209600, // 14天
|
||||
AccessToken: 3600,
|
||||
AuthorizationCode: 600,
|
||||
IdToken: 3600,
|
||||
RefreshToken: 1209600,
|
||||
BackchannelAuthenticationRequest: 600,
|
||||
ClientCredentials: 600,
|
||||
DeviceCode: 600,
|
||||
Grant: 1209600,
|
||||
Interaction: 3600,
|
||||
Session: 1209600,
|
||||
RegistrationAccessToken: 3600,
|
||||
DPoPProof: 300,
|
||||
PushedAuthorizationRequest: 600,
|
||||
ReplayDetection: 3600,
|
||||
LogoutToken: 600,
|
||||
},
|
||||
findAccount: async (ctx, id) => {
|
||||
const user = await prisma.user.findUnique({ where: { id } });
|
||||
if (!user) return undefined;
|
||||
return {
|
||||
accountId: user.id,
|
||||
async claims() {
|
||||
return {
|
||||
sub: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Provider } from 'oidc-provider';
|
||||
import config from './config';
|
||||
|
||||
const oidc = new Provider('http://localhost:4000', config);
|
||||
const oidc = new Provider('http://localhost:3000', config);
|
||||
|
||||
export default oidc;
|
|
@ -0,0 +1,86 @@
|
|||
import type { Adapter, AdapterPayload } from 'oidc-provider';
|
||||
import redis from '../redis';
|
||||
|
||||
export class RedisAdapter implements Adapter {
|
||||
name: string;
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
key(id: string) {
|
||||
return `${this.name}:${id}`;
|
||||
}
|
||||
|
||||
async upsert(id: string, payload: AdapterPayload, expiresIn: number) {
|
||||
const key = this.key(id);
|
||||
await redis.set(key, JSON.stringify(payload), 'EX', expiresIn);
|
||||
if (payload && payload.grantId) {
|
||||
// 记录grantId到id的映射,便于revokeByGrantId
|
||||
await redis.sadd(this.grantKey(payload.grantId), id);
|
||||
await redis.expire(this.grantKey(payload.grantId), expiresIn);
|
||||
}
|
||||
if (payload && payload.userCode) {
|
||||
await redis.set(this.userCodeKey(payload.userCode), id, 'EX', expiresIn);
|
||||
}
|
||||
if (payload && payload.uid) {
|
||||
await redis.set(this.uidKey(payload.uid), id, 'EX', expiresIn);
|
||||
}
|
||||
}
|
||||
|
||||
async find(id: string) {
|
||||
const data = await redis.get(this.key(id));
|
||||
return data ? JSON.parse(data) : undefined;
|
||||
}
|
||||
|
||||
async findByUserCode(userCode: string) {
|
||||
const id = await redis.get(this.userCodeKey(userCode));
|
||||
return id ? this.find(id) : undefined;
|
||||
}
|
||||
|
||||
async findByUid(uid: string) {
|
||||
const id = await redis.get(this.uidKey(uid));
|
||||
return id ? this.find(id) : undefined;
|
||||
}
|
||||
|
||||
async destroy(id: string) {
|
||||
const data = await this.find(id);
|
||||
await redis.del(this.key(id));
|
||||
if (data && data.grantId) {
|
||||
await redis.srem(this.grantKey(data.grantId), id);
|
||||
}
|
||||
if (data && data.userCode) {
|
||||
await redis.del(this.userCodeKey(data.userCode));
|
||||
}
|
||||
if (data && data.uid) {
|
||||
await redis.del(this.uidKey(data.uid));
|
||||
}
|
||||
}
|
||||
|
||||
async revokeByGrantId(grantId: string) {
|
||||
const key = this.grantKey(grantId);
|
||||
const ids = await redis.smembers(key);
|
||||
if (ids && ids.length) {
|
||||
await Promise.all(ids.map((id) => this.destroy(id)));
|
||||
}
|
||||
await redis.del(key);
|
||||
}
|
||||
|
||||
async consume(id: string) {
|
||||
const key = this.key(id);
|
||||
const data = await this.find(id);
|
||||
if (data) {
|
||||
data.consumed = Math.floor(Date.now() / 1000);
|
||||
await redis.set(key, JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
grantKey(grantId: string) {
|
||||
return `${this.name}:grant:${grantId}`;
|
||||
}
|
||||
userCodeKey(userCode: string) {
|
||||
return `${this.name}:userCode:${userCode}`;
|
||||
}
|
||||
uidKey(uid: string) {
|
||||
return `${this.name}:uid:${uid}`;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import Redis from 'ioredis';
|
|||
const redis = new Redis({
|
||||
host: 'localhost', // 根据实际情况配置
|
||||
port: 6379,
|
||||
// password: 'yourpassword', // 如有需要
|
||||
password: process.env.REDIS_PASSWORD
|
||||
});
|
||||
|
||||
export default redis;
|
|
@ -1,5 +1,6 @@
|
|||
import { z } from 'zod'
|
||||
import { initTRPC } from '@trpc/server'
|
||||
import { userRouter } from './user/user.trpc'
|
||||
|
||||
const t = initTRPC.create()
|
||||
|
||||
|
@ -7,9 +8,7 @@ export const publicProcedure = t.procedure
|
|||
export const router = t.router
|
||||
|
||||
export const appRouter = router({
|
||||
hello: publicProcedure.input(z.string().nullish()).query(({ input }) => {
|
||||
return `Hello ${input ?? 'World'}!`
|
||||
}),
|
||||
user: userRouter
|
||||
})
|
||||
|
||||
export type AppRouter = typeof appRouter
|
|
@ -1,17 +1,17 @@
|
|||
import { Hono } from "hono";
|
||||
import { createUser, searchUser } from "./userindex";
|
||||
import { createUser, searchUser } from "./user.index";
|
||||
|
||||
const userRoute = new Hono();
|
||||
const userRest = new Hono();
|
||||
|
||||
userRoute.post('/', async (c) => {
|
||||
userRest.post('/', async (c) => {
|
||||
const user = await c.req.json();
|
||||
const result = await createUser(user);
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
userRoute.get('/search', async (c) => {
|
||||
userRest.get('/search', async (c) => {
|
||||
const q = c.req.query('q') || '';
|
||||
const result = await searchUser(q);
|
||||
return c.json(result.hits.hits);
|
||||
});
|
||||
export default userRoute;
|
||||
export default userRest;
|
|
@ -1,46 +1,47 @@
|
|||
'use client';
|
||||
|
||||
import { api } from '@repo/client';
|
||||
import { Button } from '@repo/ui/components/button';
|
||||
import { useState } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { httpBatchLink } from '@trpc/client';
|
||||
// import { api } from '@repo/client';
|
||||
// import { Button } from '@repo/ui/components/button';
|
||||
// import { useState } from 'react';
|
||||
// import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
// import { httpBatchLink } from '@trpc/client';
|
||||
|
||||
function HomeContent() {
|
||||
const [name, setName] = useState('');
|
||||
const helloQuery = api.hello.useQuery(name || undefined);
|
||||
// function HomeContent() {
|
||||
// const [name, setName] = useState('');
|
||||
// const helloQuery = api.hello.useQuery(name || undefined);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="输入名字"
|
||||
className="border p-2 mr-2"
|
||||
/>
|
||||
<Button onClick={() => helloQuery.refetch()}>{helloQuery.isLoading ? '加载中...' : helloQuery.data}</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// return (
|
||||
// <div className="p-4">
|
||||
// <input
|
||||
// type="text"
|
||||
// value={name}
|
||||
// onChange={(e) => setName(e.target.value)}
|
||||
// placeholder="输入名字"
|
||||
// className="border p-2 mr-2"
|
||||
// />
|
||||
// <Button onClick={() => helloQuery.refetch()}>{helloQuery.isLoading ? '加载中...' : helloQuery.data}</Button>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function Home() {
|
||||
const [queryClient] = useState(() => new QueryClient());
|
||||
const [trpcClient] = useState(() =>
|
||||
api.createClient({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: 'http://localhost:3000/api/trpc',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
// const [queryClient] = useState(() => new QueryClient());
|
||||
// const [trpcClient] = useState(() =>
|
||||
// api.createClient({
|
||||
// links: [
|
||||
// httpBatchLink({
|
||||
// url: 'http://localhost:3000/api/trpc',
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// );
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<api.Provider client={trpcClient} queryClient={queryClient}>
|
||||
<HomeContent />
|
||||
</api.Provider>
|
||||
</QueryClientProvider>
|
||||
<div>123</div>
|
||||
// <QueryClientProvider client={queryClient}>
|
||||
// <api.Provider client={trpcClient} queryClient={queryClient}>
|
||||
// <HomeContent />
|
||||
// </api.Provider>
|
||||
// </QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ export default function QueryProvider({ children }) {
|
|||
const accessToken = '';
|
||||
|
||||
// 使用Next.js环境变量
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
||||
|
||||
// Set the default query options including staleTime.
|
||||
const [queryClient] = useState(
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `active` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `client_id` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `client_name` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `client_secret` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `client_uri` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `contacts` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `created_by` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `created_time` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `grant_types` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `jwks` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `jwks_uri` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `last_modified_time` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `logo_uri` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `policy_uri` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `post_logout_redirect_uris` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `redirect_uris` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `require_pkce` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `response_types` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `token_endpoint_auth_method` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `tos_uri` on the `oidc_clients` table. All the data in the column will be lost.
|
||||
- You are about to drop the `oidc_authorization_codes` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `oidc_consents` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `oidc_key_pairs` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `oidc_sessions` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `oidc_tokens` table. If the table is not empty, all the data it contains will be lost.
|
||||
- A unique constraint covering the columns `[clientId]` on the table `oidc_clients` will be added. If there are existing duplicate values, this will fail.
|
||||
- Added the required column `clientId` to the `oidc_clients` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `clientSecret` to the `oidc_clients` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `grantTypes` to the `oidc_clients` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `redirectUris` to the `oidc_clients` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `responseTypes` to the `oidc_clients` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `updatedAt` to the `oidc_clients` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "oidc_authorization_codes" DROP CONSTRAINT "oidc_authorization_codes_client_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "oidc_consents" DROP CONSTRAINT "oidc_consents_client_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "oidc_tokens" DROP CONSTRAINT "oidc_tokens_client_id_fkey";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "oidc_clients_client_id_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "oidc_clients" DROP COLUMN "active",
|
||||
DROP COLUMN "client_id",
|
||||
DROP COLUMN "client_name",
|
||||
DROP COLUMN "client_secret",
|
||||
DROP COLUMN "client_uri",
|
||||
DROP COLUMN "contacts",
|
||||
DROP COLUMN "created_by",
|
||||
DROP COLUMN "created_time",
|
||||
DROP COLUMN "grant_types",
|
||||
DROP COLUMN "jwks",
|
||||
DROP COLUMN "jwks_uri",
|
||||
DROP COLUMN "last_modified_time",
|
||||
DROP COLUMN "logo_uri",
|
||||
DROP COLUMN "policy_uri",
|
||||
DROP COLUMN "post_logout_redirect_uris",
|
||||
DROP COLUMN "redirect_uris",
|
||||
DROP COLUMN "require_pkce",
|
||||
DROP COLUMN "response_types",
|
||||
DROP COLUMN "token_endpoint_auth_method",
|
||||
DROP COLUMN "tos_uri",
|
||||
ADD COLUMN "clientId" TEXT NOT NULL,
|
||||
ADD COLUMN "clientSecret" TEXT NOT NULL,
|
||||
ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "grantTypes" TEXT NOT NULL,
|
||||
ADD COLUMN "redirectUris" TEXT NOT NULL,
|
||||
ADD COLUMN "responseTypes" TEXT NOT NULL,
|
||||
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "oidc_authorization_codes";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "oidc_consents";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "oidc_key_pairs";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "oidc_sessions";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "oidc_tokens";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "oidc_clients_clientId_key" ON "oidc_clients"("clientId");
|
|
@ -1,3 +1,3 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
|
|
|
@ -99,120 +99,16 @@ model UserLastVisit {
|
|||
@@map("user_last_visit")
|
||||
}
|
||||
|
||||
// OIDC 客户端相关模型
|
||||
model OidcClient {
|
||||
id String @id @default(cuid())
|
||||
clientId String @unique @map("client_id")
|
||||
clientSecret String? @map("client_secret")
|
||||
clientName String @map("client_name")
|
||||
clientUri String? @map("client_uri")
|
||||
logoUri String? @map("logo_uri")
|
||||
contacts String[]
|
||||
redirectUris String[] @map("redirect_uris")
|
||||
postLogoutRedirectUris String[] @map("post_logout_redirect_uris")
|
||||
tokenEndpointAuthMethod String @map("token_endpoint_auth_method")
|
||||
grantTypes String[] @map("grant_types")
|
||||
responseTypes String[] @map("response_types")
|
||||
scope String
|
||||
jwksUri String? @map("jwks_uri")
|
||||
jwks String?
|
||||
policyUri String? @map("policy_uri")
|
||||
tosUri String? @map("tos_uri")
|
||||
requirePkce Boolean @default(false) @map("require_pkce")
|
||||
active Boolean @default(true)
|
||||
createdBy String? @map("created_by")
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
lastModifiedTime DateTime? @updatedAt @map("last_modified_time")
|
||||
|
||||
// 关联模型
|
||||
consents OidcConsent[]
|
||||
authorizationCodes OidcCode[]
|
||||
tokens OidcToken[]
|
||||
id String @id @default(cuid())
|
||||
clientId String @unique
|
||||
clientSecret String
|
||||
redirectUris String // 存储为JSON字符串
|
||||
grantTypes String // 存储为JSON字符串
|
||||
responseTypes String // 存储为JSON字符串
|
||||
scope String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("oidc_clients")
|
||||
}
|
||||
|
||||
// 用户同意记录
|
||||
model OidcConsent {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
clientId String @map("client_id")
|
||||
scope String
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
|
||||
// 关联
|
||||
client OidcClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, clientId])
|
||||
@@map("oidc_consents")
|
||||
}
|
||||
|
||||
// 授权码
|
||||
model OidcCode {
|
||||
id String @id @default(cuid())
|
||||
code String @unique
|
||||
userId String @map("user_id")
|
||||
clientId String @map("client_id")
|
||||
scope String
|
||||
redirectUri String @map("redirect_uri")
|
||||
codeChallenge String? @map("code_challenge")
|
||||
codeChallengeMethod String? @map("code_challenge_method")
|
||||
nonce String?
|
||||
authTime DateTime @default(now()) @map("auth_time")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
used Boolean @default(false)
|
||||
|
||||
// 关联
|
||||
client OidcClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("oidc_authorization_codes")
|
||||
}
|
||||
|
||||
// 统一令牌表(合并access和refresh token)
|
||||
model OidcToken {
|
||||
id String @id @default(cuid())
|
||||
token String @unique
|
||||
userId String @map("user_id")
|
||||
clientId String @map("client_id")
|
||||
tokenType String @map("token_type") // "access" 或 "refresh"
|
||||
scope String
|
||||
expiresAt DateTime @map("expires_at")
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
isRevoked Boolean @default(false) @map("is_revoked")
|
||||
parentId String? @map("parent_id") // 用于关联refresh token和对应的access token
|
||||
|
||||
// 关联
|
||||
client OidcClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, tokenType, isRevoked])
|
||||
@@map("oidc_tokens")
|
||||
}
|
||||
|
||||
// Session管理
|
||||
model OidcSession {
|
||||
id String @id @default(cuid())
|
||||
sessionId String @unique @map("session_id")
|
||||
userId String @map("user_id")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
lastActive DateTime @default(now()) @map("last_active")
|
||||
deviceInfo String? @map("device_info")
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
lastModifiedTime DateTime? @updatedAt @map("last_modified_time")
|
||||
|
||||
@@map("oidc_sessions")
|
||||
}
|
||||
|
||||
// 供应商的密钥对
|
||||
model OidcKeyPair {
|
||||
id String @id @default(cuid())
|
||||
kid String @unique
|
||||
privateKey String @map("private_key")
|
||||
publicKey String @map("public_key")
|
||||
algorithm String
|
||||
active Boolean @default(true)
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
|
||||
@@map("oidc_key_pairs")
|
||||
}
|
||||
|
|
|
@ -53,6 +53,9 @@ importers:
|
|||
ioredis:
|
||||
specifier: 5.4.1
|
||||
version: 5.4.1
|
||||
jose:
|
||||
specifier: ^6.0.11
|
||||
version: 6.0.11
|
||||
minio:
|
||||
specifier: 7.1.3
|
||||
version: 7.1.3
|
||||
|
|
Loading…
Reference in New Issue