feat: add github auth integrate
This commit is contained in:
parent
03bc9b5125
commit
7f7c95b11c
@ -4,6 +4,7 @@ import { useUserStore } from '@/store/user';
|
||||
import { toast } from 'sonner';
|
||||
import { trpc } from '../trpc';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { BuiltInProviderType } from '@auth/core/providers';
|
||||
|
||||
export function useAuth() {
|
||||
const trpcUtils = trpc.useUtils();
|
||||
@ -38,6 +39,32 @@ export function useAuth() {
|
||||
}
|
||||
);
|
||||
|
||||
const loginWithOAuth = useEvent(async (provider: BuiltInProviderType) => {
|
||||
let res: SignInResponse | undefined;
|
||||
try {
|
||||
res = await signIn(provider, {
|
||||
redirect: false,
|
||||
});
|
||||
console.log('res', res);
|
||||
} catch (err) {
|
||||
toast.error(t('Login failed'));
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (res?.error) {
|
||||
toast.error(t('Login failed'));
|
||||
throw new Error('Login failed');
|
||||
}
|
||||
|
||||
const userInfo = await trpcUtils.user.info.fetch();
|
||||
if (!userInfo) {
|
||||
toast.error(t('Can not get current user info'));
|
||||
throw new Error('Login failed, ');
|
||||
}
|
||||
|
||||
return userInfo;
|
||||
});
|
||||
|
||||
const logout = useEvent(async () => {
|
||||
await signOut({
|
||||
redirect: false,
|
||||
@ -49,6 +76,7 @@ export function useAuth() {
|
||||
|
||||
return {
|
||||
loginWithPassword,
|
||||
loginWithOAuth,
|
||||
logout,
|
||||
};
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ const defaultGlobalConfig: AppRouterOutput['global']['config'] = {
|
||||
allowRegister: false,
|
||||
alphaMode: false,
|
||||
disableAnonymousTelemetry: false,
|
||||
authProvider: [],
|
||||
};
|
||||
|
||||
const callAnonymousTelemetry = once(() => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';
|
||||
import { useGlobalConfig } from '@/hooks/useConfig';
|
||||
import { Form, Typography } from 'antd';
|
||||
import { Divider, Form, Typography } from 'antd';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { setUserInfo } from '@/store/user';
|
||||
import { z } from 'zod';
|
||||
@ -8,6 +8,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useAuth } from '@/api/authjs/useAuth';
|
||||
import { useEventWithLoading } from '@/hooks/useEvent';
|
||||
import { LuGithub } from 'react-icons/lu';
|
||||
|
||||
export const Route = createFileRoute('/login')({
|
||||
validateSearch: z.object({
|
||||
@ -29,7 +30,7 @@ function LoginComponent() {
|
||||
const { t } = useTranslation();
|
||||
const search = Route.useSearch();
|
||||
|
||||
const { loginWithPassword } = useAuth();
|
||||
const { loginWithPassword, loginWithOAuth } = useAuth();
|
||||
|
||||
const [handleLogin, loading] = useEventWithLoading(async (values: any) => {
|
||||
const userInfo = await loginWithPassword(values.username, values.password);
|
||||
@ -41,7 +42,7 @@ function LoginComponent() {
|
||||
replace: true,
|
||||
});
|
||||
});
|
||||
const { allowRegister } = useGlobalConfig();
|
||||
const { allowRegister, authProvider } = useGlobalConfig();
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center dark:bg-gray-900">
|
||||
@ -96,6 +97,22 @@ function LoginComponent() {
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
{authProvider.length > 0 && (
|
||||
<>
|
||||
<Divider>{t('Or')}</Divider>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="h-12 w-12 p-3"
|
||||
onClick={() => loginWithOAuth('github')}
|
||||
>
|
||||
<LuGithub size={24} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Auth, AuthConfig, createActionURL } from '@auth/core';
|
||||
import Nodemailer from '@auth/core/providers/nodemailer';
|
||||
import Credentials from '@auth/core/providers/credentials';
|
||||
import Github from '@auth/core/providers/github';
|
||||
import { env } from '../utils/env.js';
|
||||
import { prisma } from './_client.js';
|
||||
import type { PrismaClient, Prisma, User } from '@prisma/client';
|
||||
@ -28,7 +29,8 @@ export interface UserAuthPayload {
|
||||
export const authConfig: Omit<AuthConfig, 'raw'> = {
|
||||
debug: env.isProd ? false : true,
|
||||
basePath: '/api/auth',
|
||||
providers: [
|
||||
trustHost: true,
|
||||
providers: _.compact([
|
||||
Credentials({
|
||||
id: 'account',
|
||||
name: 'Account',
|
||||
@ -52,28 +54,41 @@ export const authConfig: Omit<AuthConfig, 'raw'> = {
|
||||
return toAdapterUser(user);
|
||||
},
|
||||
}),
|
||||
Nodemailer({
|
||||
id: 'email',
|
||||
name: 'Email',
|
||||
...env.auth.email,
|
||||
async sendVerificationRequest(params) {
|
||||
const { identifier, url, provider, theme } = params;
|
||||
const { host } = new URL(url);
|
||||
const transport = createTransport(provider.server);
|
||||
const result = await transport.sendMail({
|
||||
to: identifier,
|
||||
from: provider.from,
|
||||
subject: `Sign in Tianji to ${host}`,
|
||||
text: `Sign in Tianji to ${host}\n${url}\n\n`,
|
||||
html: nodemailHtmlBody({ url, host, theme }),
|
||||
});
|
||||
const failed = result.rejected.concat(result.pending).filter(Boolean);
|
||||
if (failed.length) {
|
||||
throw new Error(`Email (${failed.join(', ')}) could not be sent`);
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
env.auth.provider.includes('email') &&
|
||||
Nodemailer({
|
||||
id: 'email',
|
||||
name: 'Email',
|
||||
...env.auth.email,
|
||||
async sendVerificationRequest(params) {
|
||||
const { identifier, url, provider, theme } = params;
|
||||
const { host } = new URL(url);
|
||||
const transport = createTransport(provider.server);
|
||||
const result = await transport.sendMail({
|
||||
to: identifier,
|
||||
from: provider.from,
|
||||
subject: `Sign in Tianji to ${host}`,
|
||||
text: `Sign in Tianji to ${host}\n${url}\n\n`,
|
||||
html: nodemailHtmlBody({ url, host, theme }),
|
||||
});
|
||||
const failed = result.rejected.concat(result.pending).filter(Boolean);
|
||||
if (failed.length) {
|
||||
throw new Error(`Email (${failed.join(', ')}) could not be sent`);
|
||||
}
|
||||
},
|
||||
}),
|
||||
env.auth.provider.includes('github') &&
|
||||
Github({
|
||||
id: 'github',
|
||||
name: 'Github',
|
||||
...env.auth.github,
|
||||
}),
|
||||
env.auth.provider.includes('google') &&
|
||||
Github({
|
||||
id: 'google',
|
||||
name: 'Google',
|
||||
...env.auth.google,
|
||||
}),
|
||||
]),
|
||||
adapter: TianjiPrismaAdapter(prisma),
|
||||
secret: env.auth.secret,
|
||||
session: {
|
||||
|
@ -23,6 +23,7 @@ export const globalRouter = router({
|
||||
alphaMode: z.boolean(),
|
||||
disableAnonymousTelemetry: z.boolean(),
|
||||
customTrackerScriptName: z.string().optional(),
|
||||
authProvider: z.array(z.string()),
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
@ -34,6 +35,7 @@ export const globalRouter = router({
|
||||
alphaMode: env.alphaMode,
|
||||
disableAnonymousTelemetry: env.disableAnonymousTelemetry,
|
||||
customTrackerScriptName: env.customTrackerScriptName,
|
||||
authProvider: env.auth.provider,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { v1 as uuid } from 'uuid';
|
||||
import md5 from 'md5';
|
||||
import _ from 'lodash';
|
||||
|
||||
const jwtSecret =
|
||||
!process.env.JWT_SECRET ||
|
||||
@ -13,11 +14,24 @@ export const env = {
|
||||
jwtSecret,
|
||||
port: Number(process.env.PORT || 12345),
|
||||
auth: {
|
||||
provider: _.compact([
|
||||
!!process.env.EMAIL_SERVER && 'email',
|
||||
!!process.env.AUTH_GITHUB_ID && 'github',
|
||||
!!process.env.AUTH_GOOGLE_ID && 'google',
|
||||
]),
|
||||
secret: process.env.AUTH_SECRET || md5(jwtSecret),
|
||||
email: {
|
||||
server: process.env.EMAIL_SERVER,
|
||||
from: process.env.EMAIL_FROM,
|
||||
},
|
||||
github: {
|
||||
clientId: process.env.AUTH_GITHUB_ID,
|
||||
clientSecret: process.env.AUTH_GITHUB_SECRET,
|
||||
},
|
||||
google: {
|
||||
clientId: process.env.AUTH_GOOGLE_ID,
|
||||
clientSecret: process.env.AUTH_GOOGLE_SECRET,
|
||||
},
|
||||
},
|
||||
allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER),
|
||||
allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI ?? 'true'),
|
||||
|
Loading…
Reference in New Issue
Block a user