-
+
{socialNetworkName}
{isLinked ? (
diff --git a/src/components/profile/edit-profile-slide-over/edit-profile-slide-over.tsx b/src/components/profile/edit-profile-slide-over/edit-profile-slide-over.tsx
index 80753d6..398874e 100644
--- a/src/components/profile/edit-profile-slide-over/edit-profile-slide-over.tsx
+++ b/src/components/profile/edit-profile-slide-over/edit-profile-slide-over.tsx
@@ -96,8 +96,7 @@ export default function EditProfileSlideOver(props: EditProfileSlideOverProps) {
)
}
>
-
-
+
diff --git a/src/components/profile/profile-body/profile-body.tsx b/src/components/profile/profile-body/profile-body.tsx
index fe77489..27c2e74 100644
--- a/src/components/profile/profile-body/profile-body.tsx
+++ b/src/components/profile/profile-body/profile-body.tsx
@@ -24,7 +24,7 @@ export default function ProfileBody() {
if (router.query.open === 'socials') {
editProfile.open()
}
- }, [editProfile, router.query.open])
+ }, [router.query.open])
const profileInfo: { name: string; value: ReactNode }[] = user
? [
diff --git a/src/components/seo/default-seo.tsx b/src/components/seo/default-seo.tsx
index d74fe82..d0bf8ed 100644
--- a/src/components/seo/default-seo.tsx
+++ b/src/components/seo/default-seo.tsx
@@ -7,12 +7,12 @@ type DefaultSeoProps = Partial
, 'title
export default function DefaultSeo(props: DefaultSeoProps) {
return (
{
+ switch (provider) {
+ case 'mirea':
+ return '/assets/providers/mirea.svg'
+ case 'google':
+ return '/assets/providers/google.svg'
+ case 'github':
+ return '/assets/providers/github.svg'
+ default:
+ return '/assets/providers/mirea.svg'
+ }
+}
export default function SignIn() {
const router = useRouter()
const callbackUrl = router.query.callbackUrl ? (router.query.callbackUrl as string) : '/'
const error = router.query.error
- const providers: { id: string; name: string; image: string }[] = [
- {
- id: 'mirea',
- name: 'ЛКC',
- image: '/assets/providers/mirea.svg',
- },
- {
- id: 'google',
- name: 'Google',
- image: '/assets/providers/google.svg',
+ const [providers, setProviders] = React.useState<{ id: string; name: string; image: string }[]>(
+ [],
+ )
+
+ useQuery(
+ ['providers'],
+ async () => {
+ const providers = await getProviders()
+ return providers
},
{
- id: 'github',
- name: 'GitHub',
- image: '/assets/providers/github.svg',
+ onSuccess: (providers) => {
+ if (!providers) return
+
+ setProviders(
+ Object.values(providers).map((provider) => ({
+ id: provider.id,
+ name: provider.name,
+ image: getProviderImage(provider.id),
+ })),
+ )
+ },
},
- ]
+ )
return (
diff --git a/src/env.mjs b/src/env.mjs
index 48008c2..a823e42 100644
--- a/src/env.mjs
+++ b/src/env.mjs
@@ -25,12 +25,18 @@ export const env = createEnv({
REDIS_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(1),
NEXTAUTH_URL: z.string().url(),
- MIREA_CLIENT_ID: z.string().min(1),
- MIREA_CLIENT_SECRET: z.string().min(1),
- GOOGLE_CLIENT_ID: z.string().min(1),
- GOOGLE_CLIENT_SECRET: z.string().min(1),
- GITHUB_CLIENT_ID: z.string().min(1),
- GITHUB_CLIENT_SECRET: z.string().min(1),
+
+ MIREA_CLIENT_ID: z.string().nullish(),
+ MIREA_CLIENT_SECRET: z.string().nullish(),
+
+ MIREA_LKS_CLIENT_ID: z.string().nullish(),
+ MIREA_LKS_CLIENT_SECRET: z.string().nullish(),
+
+ GOOGLE_CLIENT_ID: z.string().nullish(),
+ GOOGLE_CLIENT_SECRET: z.string().nullish(),
+
+ GITHUB_CLIENT_ID: z.string().nullish(),
+ GITHUB_CLIENT_SECRET: z.string().nullish(),
CALLBACK_URL: z.string().url(),
CALLBACK_SECRET_URL_STRING: z.string().min(1).max(32),
@@ -86,6 +92,10 @@ export const env = createEnv({
MIREA_CLIENT_ID: process.env.MIREA_CLIENT_ID,
MIREA_CLIENT_SECRET: process.env.MIREA_CLIENT_SECRET,
+
+ MIREA_LKS_CLIENT_ID: process.env.MIREA_LKS_CLIENT_ID,
+ MIREA_LKS_CLIENT_SECRET: process.env.MIREA_LKS_CLIENT_SECRET,
+
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
diff --git a/src/server/api/routers/posts.ts b/src/server/api/routers/posts.ts
index acd6ce1..bffab4f 100644
--- a/src/server/api/routers/posts.ts
+++ b/src/server/api/routers/posts.ts
@@ -354,6 +354,7 @@ async function searchPosts(query: string, reason: PostItemReason) {
name: true,
description: true,
reason: true,
+ slug: true,
},
where: {
status,
@@ -376,6 +377,7 @@ async function searchPosts(query: string, reason: PostItemReason) {
name: true,
description: true,
reason: true,
+ slug: true,
},
where: {
status,
diff --git a/src/server/auth-providers/mirea-ninja-lks-provider.ts b/src/server/auth-providers/mirea-ninja-lks-provider.ts
index 63c7d30..9b14f8c 100644
--- a/src/server/auth-providers/mirea-ninja-lks-provider.ts
+++ b/src/server/auth-providers/mirea-ninja-lks-provider.ts
@@ -21,8 +21,8 @@ type MireaNinjaLKSProviderConfig = Required, 'clientId' |
export default function MireaNinjaLksProvider(options: MireaNinjaLKSProviderConfig): Provider {
return {
- id: 'mirea',
- name: 'Mirea',
+ id: 'lks',
+ name: 'LKS',
type: 'oauth',
version: '2.0',
accessTokenUrl: 'https://auth-app.mirea.ru/oauth/token',
@@ -51,7 +51,6 @@ export default function MireaNinjaLksProvider(options: MireaNinjaLKSProviderConf
image: 'https://lk.mirea.ru' + profile.arUser.PHOTO,
isBlocked: false,
blockReason: null,
- secretSocialNetworksAuthPayload: '',
}
},
clientId: options.clientId,
diff --git a/src/server/auth-providers/mirea-provider.ts b/src/server/auth-providers/mirea-provider.ts
new file mode 100644
index 0000000..31110f6
--- /dev/null
+++ b/src/server/auth-providers/mirea-provider.ts
@@ -0,0 +1,53 @@
+import type { OAuthConfig, Provider } from 'next-auth/providers'
+import { nicknameValidation } from '@/lib/nickname-validation'
+import { Role } from '@prisma/client'
+
+interface UserInfo {
+ username: string
+ email: string
+ name: string
+ lastname: string
+ middlename: string
+ uid: string
+}
+
+type MireaProviderConfig = Required, 'clientId' | 'clientSecret'>>
+
+export default function MireaProvider(options: MireaProviderConfig): Provider {
+ return {
+ id: 'mirea',
+ name: 'RTU MIREA',
+ type: 'oauth',
+ version: '2.0',
+ accessTokenUrl: 'https://login.mirea.ru/oauth2/v1/token/',
+ requestTokenUrl: 'https://login.mirea.ru/oauth2/v1/token/',
+ authorization: {
+ url: 'https://login.mirea.ru/oauth2/v1/authorize/',
+ params: { scope: 'basic' },
+ },
+ token: {
+ url: 'https://login.mirea.ru/oauth2/v1/token/',
+ },
+ userinfo: {
+ url: 'https://login.mirea.ru/resources/v1/userinfo',
+ },
+ checks: ['state'],
+ async profile(profile: UserInfo) {
+ const name = [profile.name, profile.lastname].join(' ')
+ return {
+ id: profile.uid,
+ name,
+ nickname: await nicknameValidation(name),
+ email: profile.email,
+ emailVerified: new Date(),
+ userInfo: null,
+ role: Role.USER,
+ image: null,
+ isBlocked: false,
+ blockReason: null,
+ }
+ },
+ clientId: options.clientId,
+ clientSecret: options.clientSecret,
+ }
+}
diff --git a/src/server/auth.ts b/src/server/auth.ts
index ae095bb..d6b808d 100644
--- a/src/server/auth.ts
+++ b/src/server/auth.ts
@@ -9,6 +9,7 @@ import { Role } from '@prisma/client'
import { env } from '@/env.mjs'
import { type User as PrismaUser } from '@prisma/client'
import MireaNinjaLksProvider from '@/server/auth-providers/mirea-ninja-lks-provider'
+import MireaProvider from './auth-providers/mirea-provider'
/**
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
@@ -17,12 +18,82 @@ import MireaNinjaLksProvider from '@/server/auth-providers/mirea-ninja-lks-provi
* @see https://next-auth.js.org/getting-started/typescript#module-augmentation
*/
declare module 'next-auth' {
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
+ interface User extends Omit {}
+
interface Session {
user: User
}
+}
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
- interface User extends PrismaUser {}
+const getProviders = () => {
+ const providers = []
+ if (env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET) {
+ providers.push(
+ GithubProvider({
+ clientId: env.GITHUB_CLIENT_ID,
+ clientSecret: env.GITHUB_CLIENT_SECRET,
+ async profile(profile: GithubProfile) {
+ return {
+ id: profile.id.toString(),
+ name: profile.name ?? profile.login,
+ nickname: await nicknameValidation(profile.login),
+ email: profile.email,
+ emailVerified: new Date(),
+ userInfo: null,
+ role: Role.USER,
+ image: profile.avatar_url,
+ isBlocked: false,
+ blockReason: null,
+ }
+ },
+ }),
+ )
+ }
+ if (env.MIREA_CLIENT_ID && env.MIREA_CLIENT_SECRET) {
+ providers.push(
+ MireaProvider({ clientId: env.MIREA_CLIENT_ID, clientSecret: env.MIREA_CLIENT_SECRET }),
+ )
+ }
+ if (env.MIREA_LKS_CLIENT_ID && env.MIREA_LKS_CLIENT_SECRET) {
+ providers.push(
+ MireaNinjaLksProvider({
+ clientId: env.MIREA_LKS_CLIENT_ID,
+ clientSecret: env.MIREA_LKS_CLIENT_SECRET,
+ }),
+ )
+ }
+ if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
+ providers.push(
+ GoogleProvider({
+ clientId: env.GOOGLE_CLIENT_ID,
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
+ authorization: {
+ params: {
+ prompt: 'consent',
+ access_type: 'offline',
+ response_type: 'code',
+ },
+ },
+ async profile(profile: GoogleProfile) {
+ return {
+ id: profile.sub,
+ name: profile.name,
+ nickname: await nicknameValidation(profile.email.split('@')[0] ?? ''),
+ email: profile.email,
+ emailVerified: new Date(),
+ userInfo: null,
+ role: Role.USER,
+ image: profile.picture,
+ isBlocked: false,
+ blockReason: null,
+ }
+ },
+ }),
+ )
+ }
+
+ return providers
}
/**
@@ -52,52 +123,7 @@ export const authOptions: NextAuthOptions = {
},
adapter: PrismaAdapter(prisma),
providers: [
- GithubProvider({
- clientId: env.GITHUB_CLIENT_ID,
- clientSecret: env.GITHUB_CLIENT_SECRET,
- async profile(profile: GithubProfile) {
- return {
- id: profile.id.toString(),
- name: profile.name ?? profile.login,
- nickname: await nicknameValidation(profile.login),
- email: profile.email,
- emailVerified: new Date(),
- userInfo: null,
- role: Role.USER,
- image: profile.avatar_url,
- isBlocked: false,
- blockReason: null,
- secretSocialNetworksAuthPayload: '',
- }
- },
- }),
- GoogleProvider({
- clientId: env.GOOGLE_CLIENT_ID,
- clientSecret: env.GOOGLE_CLIENT_SECRET,
- authorization: {
- params: {
- prompt: 'consent',
- access_type: 'offline',
- response_type: 'code',
- },
- },
- async profile(profile: GoogleProfile) {
- return {
- id: profile.sub,
- name: profile.name,
- nickname: await nicknameValidation(profile.email.split('@')[0] ?? ''),
- email: profile.email,
- emailVerified: new Date(),
- userInfo: null,
- role: Role.USER,
- image: profile.picture,
- isBlocked: false,
- blockReason: null,
- secretSocialNetworksAuthPayload: '',
- }
- },
- }),
- MireaNinjaLksProvider({ clientId: env.MIREA_CLIENT_ID, clientSecret: env.MIREA_CLIENT_SECRET }),
+ ...getProviders(),
/**
* ...add more providers here.
*