From dd95a41a647e021eb834cbca52e42192c17a54ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Kadir=20Ak=C4=B1n?= Date: Thu, 11 Dec 2025 01:09:51 +0300 Subject: [PATCH] chore(auth): update OAuth login descriptions in multiple languages --- .env.example | 3 ++ messages/ar.json | 1 + messages/en.json | 1 + messages/es.json | 1 + messages/ja.json | 1 + messages/tr.json | 1 + messages/zh.json | 1 + prompts.config.ts | 6 +-- src/app/(auth)/login/page.tsx | 21 ++++++--- src/app/(auth)/register/page.tsx | 13 +++++- src/app/api/auth/register/route.ts | 10 +++++ src/app/layout.tsx | 2 +- src/components/auth/auth-content.tsx | 32 ++++++++++++++ src/components/auth/oauth-button.tsx | 65 ++++++++++++++++++++++++++++ src/components/layout/header.tsx | 14 ++++-- src/lib/config/index.ts | 2 +- src/lib/plugins/auth/github.ts | 12 +++++ src/lib/plugins/auth/index.ts | 4 +- src/lib/plugins/index.ts | 2 +- 19 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 src/components/auth/auth-content.tsx create mode 100644 src/components/auth/oauth-button.tsx create mode 100644 src/lib/plugins/auth/github.ts diff --git a/.env.example b/.env.example index 5786fec3..2983139d 100644 --- a/.env.example +++ b/.env.example @@ -18,3 +18,6 @@ NEXTAUTH_SECRET="your-super-secret-key-change-in-production" # S3_ACCESS_KEY_ID="" # S3_SECRET_ACCESS_KEY="" # S3_ENDPOINT="" # For S3-compatible services like MinIO + +# GITHUB_CLIENT_ID=your_client_id +# GITHUB_CLIENT_SECRET=your_client_secret \ No newline at end of file diff --git a/messages/ar.json b/messages/ar.json index df8a92f6..2b88d407 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -45,6 +45,7 @@ "auth": { "login": "تسجيل الدخول", "loginDescription": "أدخل بياناتك للمتابعة", + "loginDescriptionOAuth": "سجل الدخول بحسابك للمتابعة", "register": "إنشاء حساب", "registerDescription": "أنشئ حساباً للبدء", "logout": "تسجيل الخروج", diff --git a/messages/en.json b/messages/en.json index b0ac502b..5342deb9 100644 --- a/messages/en.json +++ b/messages/en.json @@ -45,6 +45,7 @@ "auth": { "login": "Login", "loginDescription": "Enter your credentials to continue", + "loginDescriptionOAuth": "Sign in with your account to continue", "register": "Register", "registerDescription": "Create an account to get started", "logout": "Logout", diff --git a/messages/es.json b/messages/es.json index 50483d35..dceed079 100644 --- a/messages/es.json +++ b/messages/es.json @@ -45,6 +45,7 @@ "auth": { "login": "Iniciar sesión", "loginDescription": "Ingresa tus credenciales para continuar", + "loginDescriptionOAuth": "Inicia sesión con tu cuenta para continuar", "register": "Registrarse", "registerDescription": "Crea una cuenta para comenzar", "logout": "Cerrar sesión", diff --git a/messages/ja.json b/messages/ja.json index 5975f636..cb8a3b81 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -45,6 +45,7 @@ "auth": { "login": "ログイン", "loginDescription": "続行するには認証情報を入力してください", + "loginDescriptionOAuth": "アカウントでサインインして続行", "register": "登録", "registerDescription": "アカウントを作成して始めましょう", "logout": "ログアウト", diff --git a/messages/tr.json b/messages/tr.json index eaae3b4d..f1727ec0 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -45,6 +45,7 @@ "auth": { "login": "Giriş Yap", "loginDescription": "Devam etmek için bilgilerinizi girin", + "loginDescriptionOAuth": "Devam etmek için hesabınızla giriş yapın", "register": "Kayıt Ol", "registerDescription": "Başlamak için bir hesap oluşturun", "logout": "Çıkış Yap", diff --git a/messages/zh.json b/messages/zh.json index f9331e74..d7eec5a6 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -45,6 +45,7 @@ "auth": { "login": "登录", "loginDescription": "输入您的凭据以继续", + "loginDescriptionOAuth": "使用您的账户登录以继续", "register": "注册", "registerDescription": "创建账户以开始使用", "logout": "退出登录", diff --git a/prompts.config.ts b/prompts.config.ts index 55ede862..ab8221e2 100644 --- a/prompts.config.ts +++ b/prompts.config.ts @@ -25,10 +25,10 @@ export default defineConfig({ // Authentication plugin auth: { - // Available: "credentials" | "google" | "azure" | custom - provider: "credentials", + // Available: "credentials" | "google" | "azure" | "github" | custom + provider: "github", // Allow public registration - allowRegistration: true, + allowRegistration: false, }, // Storage plugin for media uploads diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 5ea2a2f1..a4ef1622 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -1,7 +1,8 @@ import { Metadata } from "next"; import Link from "next/link"; import { getTranslations } from "next-intl/server"; -import { LoginForm } from "@/components/auth/login-form"; +import { getConfig } from "@/lib/config"; +import { AuthContent } from "@/components/auth/auth-content"; export const metadata: Metadata = { title: "Login", @@ -10,21 +11,27 @@ export const metadata: Metadata = { export default async function LoginPage() { const t = await getTranslations("auth"); + const config = await getConfig(); + const isCredentials = config.auth.provider === "credentials"; return (

{t("login")}

-

{t("loginDescription")}

+

+ {isCredentials ? t("loginDescription") : t("loginDescriptionOAuth")} +

- +
-

- {t("noAccount")}{" "} - {t("register")} -

+ {isCredentials && ( +

+ {t("noAccount")}{" "} + {t("register")} +

+ )}
); diff --git a/src/app/(auth)/register/page.tsx b/src/app/(auth)/register/page.tsx index 5e723041..9718d8da 100644 --- a/src/app/(auth)/register/page.tsx +++ b/src/app/(auth)/register/page.tsx @@ -1,7 +1,9 @@ import { Metadata } from "next"; import Link from "next/link"; +import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; -import { RegisterForm } from "@/components/auth/register-form"; +import { getConfig } from "@/lib/config"; +import { AuthContent } from "@/components/auth/auth-content"; export const metadata: Metadata = { title: "Register", @@ -10,6 +12,13 @@ export const metadata: Metadata = { export default async function RegisterPage() { const t = await getTranslations("auth"); + const config = await getConfig(); + const isCredentials = config.auth.provider === "credentials"; + + // Block registration if disabled or using OAuth providers + if (!isCredentials || !config.auth.allowRegistration) { + redirect("/login"); + } return (
@@ -19,7 +28,7 @@ export default async function RegisterPage() {

{t("registerDescription")}

- +

{t("hasAccount")}{" "} diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 4c9d6482..36327f4b 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -2,6 +2,7 @@ import { NextResponse } from "next/server"; import bcrypt from "bcryptjs"; import { z } from "zod"; import { db } from "@/lib/db"; +import { getConfig } from "@/lib/config"; const registerSchema = z.object({ name: z.string().min(2), @@ -12,6 +13,15 @@ const registerSchema = z.object({ export async function POST(request: Request) { try { + // Check if registration is allowed + const config = await getConfig(); + if (!config.auth.allowRegistration) { + return NextResponse.json( + { error: "registration_disabled", message: "Registration is disabled" }, + { status: 403 } + ); + } + const body = await request.json(); const parsed = registerSchema.safeParse(body); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 74897ca3..8c89a18a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -89,7 +89,7 @@ export default async function RootLayout({

-
+
{children}
diff --git a/src/components/auth/auth-content.tsx b/src/components/auth/auth-content.tsx new file mode 100644 index 00000000..30ceb336 --- /dev/null +++ b/src/components/auth/auth-content.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { LoginForm } from "./login-form"; +import { RegisterForm } from "./register-form"; +import { OAuthButton } from "./oauth-button"; + +interface AuthContentProps { + provider: string; + mode: "login" | "register"; +} + +const providerNames: Record = { + github: "GitHub", + google: "Google", + azure: "Microsoft", + credentials: "Email", +}; + +export function AuthContent({ provider, mode }: AuthContentProps) { + // For OAuth providers, show the OAuth button + if (provider !== "credentials") { + return ( + + ); + } + + // For credentials, show the appropriate form + return mode === "login" ? : ; +} diff --git a/src/components/auth/oauth-button.tsx b/src/components/auth/oauth-button.tsx new file mode 100644 index 00000000..11c9cbe0 --- /dev/null +++ b/src/components/auth/oauth-button.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { useState } from "react"; +import { signIn } from "next-auth/react"; +import { useTranslations } from "next-intl"; +import { Loader2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +interface OAuthButtonProps { + provider: string; + providerName: string; +} + +const providerIcons: Record = { + github: ( + + + + ), + google: ( + + + + + + + ), + azure: ( + + + + ), +}; + +export function OAuthButton({ provider, providerName }: OAuthButtonProps) { + const t = useTranslations("auth"); + const [isLoading, setIsLoading] = useState(false); + + const handleSignIn = async () => { + setIsLoading(true); + try { + await signIn(provider, { callbackUrl: "/" }); + } catch (error) { + console.error("Sign in error:", error); + setIsLoading(false); + } + }; + + return ( + + ); +} diff --git a/src/components/layout/header.tsx b/src/components/layout/header.tsx index 857dcdcc..e31ca913 100644 --- a/src/components/layout/header.tsx +++ b/src/components/layout/header.tsx @@ -42,7 +42,11 @@ const languages = [ { code: "ar", name: "العربية" }, ]; -export function Header() { +interface HeaderProps { + showRegister?: boolean; +} + +export function Header({ showRegister = true }: HeaderProps) { const { data: session } = useSession(); const t = useTranslations(); const { theme, setTheme } = useTheme(); @@ -230,9 +234,11 @@ export function Header() { - + {showRegister && ( + + )} )} diff --git a/src/lib/config/index.ts b/src/lib/config/index.ts index a5ea260b..4701b832 100644 --- a/src/lib/config/index.ts +++ b/src/lib/config/index.ts @@ -19,7 +19,7 @@ export interface ThemeConfig { } export interface AuthConfig { - provider: "credentials" | "google" | "azure" | string; + provider: "credentials" | "google" | "azure" | "github" | string; allowRegistration: boolean; } diff --git a/src/lib/plugins/auth/github.ts b/src/lib/plugins/auth/github.ts new file mode 100644 index 00000000..50e4f781 --- /dev/null +++ b/src/lib/plugins/auth/github.ts @@ -0,0 +1,12 @@ +import GitHub from "next-auth/providers/github"; +import type { AuthPlugin } from "../types"; + +export const githubPlugin: AuthPlugin = { + id: "github", + name: "GitHub", + getProvider: () => + GitHub({ + clientId: process.env.GITHUB_CLIENT_ID!, + clientSecret: process.env.GITHUB_CLIENT_SECRET!, + }), +}; diff --git a/src/lib/plugins/auth/index.ts b/src/lib/plugins/auth/index.ts index facd4a4c..b3da030b 100644 --- a/src/lib/plugins/auth/index.ts +++ b/src/lib/plugins/auth/index.ts @@ -2,12 +2,14 @@ import { registerAuthPlugin } from "../registry"; import { credentialsPlugin } from "./credentials"; import { googlePlugin } from "./google"; import { azurePlugin } from "./azure"; +import { githubPlugin } from "./github"; // Register all built-in auth plugins export function registerBuiltInAuthPlugins(): void { registerAuthPlugin(credentialsPlugin); registerAuthPlugin(googlePlugin); registerAuthPlugin(azurePlugin); + registerAuthPlugin(githubPlugin); } -export { credentialsPlugin, googlePlugin, azurePlugin }; +export { credentialsPlugin, googlePlugin, azurePlugin, githubPlugin }; diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index c41d2f78..0e2585c3 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -30,7 +30,7 @@ export async function getConfiguredAuthPlugin() { if (!plugin) { throw new Error( `Auth plugin "${config.auth.provider}" not found. ` + - `Available plugins: credentials, google, azure` + `Available plugins: credentials, google, azure, github` ); }