diff --git a/Controllers/AuthController.cs b/Controllers/AuthController.cs index 7da0a82..c7a2706 100644 --- a/Controllers/AuthController.cs +++ b/Controllers/AuthController.cs @@ -1,17 +1,14 @@ -using System.Security.Claims; using Foxel.Models; using Foxel.Services.Interface; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Foxel.Models.Request.Auth; using Foxel.Models.Response.Auth; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; namespace Foxel.Controllers; [Route("api/auth")] -public class AuthController(IUserService userService) : BaseApiController +public class AuthController(IUserService userService, IConfigService configService) : BaseApiController { [HttpPost("register")] public async Task>> Register([FromBody] RegisterRequest request) @@ -101,73 +98,110 @@ public class AuthController(IUserService userService) : BaseApiController } [HttpGet("github/login")] - public IActionResult GitHubLogin(string returnUrl = "/") + public IActionResult GitHubLogin() { - try - { - var properties = new AuthenticationProperties - { - RedirectUri = Url.Action("GitHubCallback", new { returnUrl }), - Items = { { "returnUrl", returnUrl } }, - // 添加超时设置 - AllowRefresh = true, - ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10), - IsPersistent = false - }; - return Challenge(properties, "GitHub"); - } - catch (Exception ex) - { - Console.WriteLine($"GitHub登录异常: {ex}"); - return Redirect("/login?error=github_login_error"); - } + string githubClientId = configService["Authentication:GitHubClientId"]; + string githubCallback = configService["Authentication:GitHubRedirectUri"]; + string githubAuthorizeUrl = + $"https://github.com/login/oauth/authorize?client_id={Uri.EscapeDataString(githubClientId)}&redirect_uri={Uri.EscapeDataString(githubCallback)}"; + return Redirect(githubAuthorizeUrl); } [HttpGet("github/callback")] - public async Task GitHubCallback(string returnUrl = "/") + public async Task>> GitHubCallback(string code) { - try + if (string.IsNullOrEmpty(code)) { - var authenticateResult = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); - if (!authenticateResult.Succeeded) - { - Console.WriteLine("GitHub认证失败: 无法获取认证结果"); - return Redirect("/login?error=github_auth_failed"); - } - // 获取GitHub用户信息 - var githubId = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; - var githubLogin = authenticateResult.Principal.FindFirst("urn:github:login")?.Value; - var githubEmail = authenticateResult.Principal.FindFirst(ClaimTypes.Email)?.Value; - - Console.WriteLine($"GitHub用户信息: ID={githubId}, Login={githubLogin}, Email={githubEmail}"); - - if (string.IsNullOrEmpty(githubId) || string.IsNullOrEmpty(githubLogin)) - { - return Redirect("/login?error=github_missing_info"); - } - - // 登出Cookie认证会话 - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - - // 查找或创建用户 - var (success, message, user) = await userService.FindOrCreateGitHubUserAsync( - githubId, githubLogin, githubEmail); - - if (!success || user == null) - { - return Redirect($"/login?error={Uri.EscapeDataString(message)}"); - } - - // 生成JWT令牌 - var token = await userService.GenerateJwtTokenAsync(user); - - // 重定向回前端,携带token参数 - return Redirect($"{returnUrl}?token={Uri.EscapeDataString(token)}"); + return Error("GitHub授权码无效"); } - catch (Exception ex) + + string githubClientId = configService["Authentication:GitHubClientId"]; + string githubClientSecret = configService["Authentication:GitHubClientSecret"]; + string githubTokenUrl = "https://github.com/login/oauth/access_token"; + string githubUserApiUrl = "https://api.github.com/user"; + + using var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("User-Agent", "Foxel"); + httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + var tokenRequestUrl = + $"{githubTokenUrl}?client_id={Uri.EscapeDataString(githubClientId)}&client_secret={Uri.EscapeDataString(githubClientSecret)}&code={Uri.EscapeDataString(code)}"; + var tokenResponse = await httpClient.PostAsync(tokenRequestUrl, null); + + if (!tokenResponse.IsSuccessStatusCode) { - Console.WriteLine($"GitHub回调处理异常: {ex}"); - return Redirect("/login?error=github_callback_error"); + var errorContent = await tokenResponse.Content.ReadAsStringAsync(); + Console.WriteLine($"获取GitHub访问令牌失败: {tokenResponse.StatusCode}, {errorContent}"); + return Error($"获取GitHub访问令牌失败: {errorContent}", (int)tokenResponse.StatusCode); } + + var tokenResponseContent = await tokenResponse.Content.ReadAsStringAsync(); + var tokenJson = System.Text.Json.JsonDocument.Parse(tokenResponseContent); + + if (!tokenJson.RootElement.TryGetProperty("access_token", out var accessTokenElement) || + accessTokenElement.GetString() == null) + { + Console.WriteLine($"GitHub响应中未找到access_token: {tokenResponseContent}"); + return Error("获取GitHub访问令牌失败,响应中未包含令牌。"); + } + + var accessToken = accessTokenElement.GetString(); + + httpClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); + + var userResponse = await httpClient.GetAsync(githubUserApiUrl); + if (!userResponse.IsSuccessStatusCode) + { + var errorContent = await userResponse.Content.ReadAsStringAsync(); + Console.WriteLine($"获取GitHub用户信息失败: {userResponse.StatusCode}, {errorContent}"); + return Error($"获取GitHub用户信息失败: {errorContent}", (int)userResponse.StatusCode); + } + + var userContent = await userResponse.Content.ReadAsStringAsync(); + var userJson = System.Text.Json.JsonDocument.Parse(userContent); + + string? githubUserId = null; + string? email = null; + string? name = null; + string? loginName = null; + + if (userJson.RootElement.TryGetProperty("id", out var idElement)) + { + githubUserId = idElement.GetInt64().ToString(); + } + + if (userJson.RootElement.TryGetProperty("email", out var emailElement)) + { + email = emailElement.GetString(); + } + + if (userJson.RootElement.TryGetProperty("name", out var nameElement)) + { + name = nameElement.GetString(); + } + + if (userJson.RootElement.TryGetProperty("login", out var loginElement)) + { + loginName = loginElement.GetString(); + } + + if (string.IsNullOrEmpty(githubUserId)) + { + return Error("无法从GitHub获取用户ID"); + } + + + var (isSuccess, message, user) = + await userService.FindOrCreateGitHubUserAsync(githubUserId, name ?? loginName, email); + + if (!isSuccess || user == null) + { + Console.WriteLine($"创建或查找GitHub用户失败: {message}"); + return Redirect( + $"/login?error=github_user_creation_failed&message={Uri.EscapeDataString(message)}"); + } + + var token = await userService.GenerateJwtTokenAsync(user); + return Redirect($"/login?token={Uri.EscapeDataString(token)}"); } } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index bc6c8d3..5b45e15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app EXPOSE 8080 +EXPOSE 8081 EXPOSE 80 FROM oven/bun:alpine AS build-frontend @@ -31,8 +32,8 @@ RUN apt-get update && apt-get install -y nginx && rm -rf /var/lib/apt/lists/* COPY --from=build-frontend /src/View/dist /var/www/html COPY /View/nginx.conf /etc/nginx/nginx.conf -RUN mkdir -p /var/lib/nginx/body /var/cache/nginx /var/run/nginx \ - && chown -R $APP_UID:$APP_UID /var/lib/nginx /var/cache/nginx /var/run/nginx /var/log/nginx /etc/nginx /var/www/html \ +RUN mkdir -p /var/lib/nginx/body /var/cache/nginx /var/run/nginx /app/Uploads \ + && chown -R $APP_UID:$APP_UID /var/lib/nginx /var/cache/nginx /var/run/nginx /var/log/nginx /etc/nginx /var/www/html /app/Uploads \ && mkdir -p /run \ && chmod 777 /run diff --git a/Extensions/ServiceCollectionExtensions.cs b/Extensions/ServiceCollectionExtensions.cs index b20dd10..d18359c 100644 --- a/Extensions/ServiceCollectionExtensions.cs +++ b/Extensions/ServiceCollectionExtensions.cs @@ -67,35 +67,6 @@ public static class ServiceCollectionExtensions IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"])) }; - }) - .AddCookie(options => - { - options.Cookie.HttpOnly = true; - options.Cookie.SameSite = SameSiteMode.Lax; - options.ExpireTimeSpan = TimeSpan.FromMinutes(60); - }) - .AddGitHub(options => - { - options.ClientId = configuration["Authentication:GitHubClientId"]; - options.ClientSecret = configuration["Authentication:GitHubClientSecret"]; - options.CallbackPath = "/api/auth/github/callback"; - options.Scope.Add("user:email"); - options.BackchannelHttpHandler = new HttpClientHandler - { - UseCookies = false, - AllowAutoRedirect = false, - MaxConnectionsPerServer = 100 - }; - options.Events = new Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents - { - OnRemoteFailure = context => - { - Console.WriteLine($"GitHub登录失败: {context.Failure}"); - context.Response.Redirect("/login?error=github_remote_error"); - context.HandleResponse(); - return Task.CompletedTask; - } - }; }); } diff --git a/Foxel.csproj b/Foxel.csproj index 3240bec..37291c6 100644 --- a/Foxel.csproj +++ b/Foxel.csproj @@ -4,10 +4,10 @@ net9.0 enable enable + Linux - diff --git a/Program.cs b/Program.cs index fde6d3a..9e1d099 100644 --- a/Program.cs +++ b/Program.cs @@ -1,5 +1,6 @@ using Foxel.Extensions; using Foxel.Services.Interface; +using Microsoft.AspNetCore.HttpOverrides; var builder = WebApplication.CreateBuilder(args); var environment = builder.Environment; @@ -14,26 +15,30 @@ builder.Services.AddHttpContextAccessor(); builder.Services.AddApplicationAuthentication(); builder.Services.AddApplicationAuthorization(); builder.Services.AddApplicationCors(); - +builder.Services.Configure(options => +{ + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + options.KnownNetworks.Clear(); + options.KnownProxies.Clear(); +}); var app = builder.Build(); -// 初始化数据库配置 using (var scope = app.Services.CreateScope()) { var initializer = scope.ServiceProvider.GetRequiredService(); await initializer.InitializeAsync(); } - +app.UseForwardedHeaders(); app.UseApplicationStaticFiles(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } -app.UseHttpsRedirection(); app.UseApplicationOpenApi(); app.UseCors("MyAllowSpecificOrigins"); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); +app.UseHttpsRedirection(); app.Run(); \ No newline at end of file diff --git a/Services/Interface/IUserService.cs b/Services/Interface/IUserService.cs index e215c7e..07483cf 100644 --- a/Services/Interface/IUserService.cs +++ b/Services/Interface/IUserService.cs @@ -11,5 +11,5 @@ public interface IUserService Task GenerateJwtTokenAsync(User user); Task GetUserByIdAsync(int userId); Task<(bool success, string message, User? user)> FindOrCreateGitHubUserAsync( - string githubId, string githubLogin, string? email); + string githubId, string? githubName, string? email); } diff --git a/Services/UserService.cs b/Services/UserService.cs index 65a1080..bf0e61b 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -97,7 +97,7 @@ public class UserService(IDbContextFactory dbContextFactory, IConfi } public async Task<(bool success, string message, User? user)> FindOrCreateGitHubUserAsync( - string githubId, string githubLogin, string? email) + string githubId, string? githubName, string? email) { if (string.IsNullOrEmpty(email)) { @@ -106,17 +106,15 @@ public class UserService(IDbContextFactory dbContextFactory, IConfi await using var context = await dbContextFactory.CreateDbContextAsync(); - // 先尝试通过邮箱查找用户 var user = await context.Users.FirstOrDefaultAsync(u => u.Email == email); if (user == null) { - // 用户不存在,创建新用户 user = new User { - UserName = $"{githubLogin}_{githubId.Substring(0, 5)}", // 创建唯一用户名 + UserName = $"{githubName}", Email = email, - PasswordHash = HashPassword(Guid.NewGuid().ToString()), + PasswordHash = HashPassword(Guid.NewGuid().ToString()), GithubId = githubId, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow @@ -125,15 +123,14 @@ public class UserService(IDbContextFactory dbContextFactory, IConfi await context.SaveChangesAsync(); return (true, "GitHub用户注册成功", user); } - else + + if (string.IsNullOrEmpty(user.GithubId)) { - if (string.IsNullOrEmpty(user.GithubId)) - { - user.GithubId = githubId; - user.UpdatedAt = DateTime.UtcNow; - await context.SaveChangesAsync(); - } - return (true, "GitHub用户登录成功", user); + user.GithubId = githubId; + user.UpdatedAt = DateTime.UtcNow; + await context.SaveChangesAsync(); } + + return (true, "GitHub用户登录成功", user); } } \ No newline at end of file diff --git a/View/nginx.conf b/View/nginx.conf index fba4868..9b1e7db 100644 --- a/View/nginx.conf +++ b/View/nginx.conf @@ -24,6 +24,7 @@ http { proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; + proxy_ssl_verify off; } location /uploads { @@ -33,6 +34,7 @@ http { proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; + proxy_ssl_verify off; } } } \ No newline at end of file diff --git a/View/src/api/authApi.ts b/View/src/api/authApi.ts index 71fc60c..0f45ddd 100644 --- a/View/src/api/authApi.ts +++ b/View/src/api/authApi.ts @@ -1,5 +1,5 @@ -import { type BaseResult, type AuthResponse, type LoginRequest, type RegisterRequest, type UserProfile } from './types'; -import { fetchApi, BASE_URL } from './fetchClient'; +import {type BaseResult, type AuthResponse, type LoginRequest, type RegisterRequest, type UserProfile} from './types'; +import {fetchApi, BASE_URL} from './fetchClient'; // 认证数据本地存储键 const TOKEN_KEY = 'token'; @@ -19,13 +19,13 @@ export async function login(data: LoginRequest): Promise { }; // 处理GitHub OAuth回调,接收token并保存 -export function handleOAuthCallback(): boolean { +export async function handleOAuthCallback(): Promise { const urlParams = new URLSearchParams(window.location.search); const token = urlParams.get('token'); const error = urlParams.get('error'); @@ -104,52 +104,40 @@ export function handleOAuthCallback(): boolean { if (error) return false; if (token) { - const githubUser = parseJwt(token); - if (githubUser) { - // 保存token - localStorage.setItem('token', token); - - // 保存用户信息 - if (githubUser.unique_name && githubUser.email) { - const user: UserProfile = { - id: parseInt(githubUser.nameid), - userName: githubUser.unique_name, - email: githubUser.email, - roleName: '' + try { + // 保存临时token,用于API调用 + localStorage.setItem(TOKEN_KEY, token); + + // 获取完整的用户信息 + const userResponse = await getCurrentUser(); + + if (userResponse.success && userResponse.data) { + // 构造完整的认证响应并保存 + const authResponse: AuthResponse = { + token: token, + user: userResponse.data }; - localStorage.setItem('user', JSON.stringify(user)); + + saveAuthData(authResponse); + + // 清除URL中的token参数 + const url = new URL(window.location.href); + url.searchParams.delete('token'); + window.history.replaceState({}, document.title, url.toString()); + + return true; } - - // 清除URL中的token参数 - const url = new URL(window.location.href); - url.searchParams.delete('token'); - window.history.replaceState({}, document.title, url.toString()); - - return true; + return false; + } catch (error) { + console.error('第三方登录处理失败:', error); + clearAuthData(); // 清除可能部分保存的数据 + return false; } } return false; } -// 解析JWT获取用户信息 -function parseJwt(token: string) { - try { - const base64Url = token.split('.')[1]; - const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); - const jsonPayload = decodeURIComponent( - atob(base64) - .split('') - .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) - .join('') - ); - return JSON.parse(jsonPayload); - } catch { - return null; - } -} - -// 获取GitHub登录URL export function getGitHubLoginUrl(): string { - return `${BASE_URL}/auth/github/login?returnUrl=${window.location.origin}/api/auth/github/callback`; -} + return `${BASE_URL}/auth/github/login`; +} \ No newline at end of file diff --git a/View/src/pages/login/Index.tsx b/View/src/pages/login/Index.tsx index 3d8b674..7b6530b 100644 --- a/View/src/pages/login/Index.tsx +++ b/View/src/pages/login/Index.tsx @@ -1,305 +1,331 @@ -import React, { useState, useEffect } from 'react'; -import { Form, Input, Button, Checkbox, Typography, Row, Col, Divider, message } from 'antd'; -import { UserOutlined, LockOutlined, GithubOutlined, GoogleOutlined } from '@ant-design/icons'; -import { useNavigate, Link } from 'react-router'; -import { login, saveAuthData, isAuthenticated, handleOAuthCallback, getGitHubLoginUrl } from '../../api'; +import React, {useState, useEffect} from 'react'; +import {Form, Input, Button, Checkbox, Typography, Row, Col, Divider, message} from 'antd'; +import {UserOutlined, LockOutlined, GithubOutlined, GoogleOutlined} from '@ant-design/icons'; +import {useNavigate, Link} from 'react-router'; +import {login, saveAuthData, isAuthenticated, handleOAuthCallback, getGitHubLoginUrl} from '../../api'; import useIsMobile from '../../hooks/useIsMobile'; -const { Title, Text } = Typography; +const {Title, Text} = Typography; const Login: React.FC = () => { - const [loading, setLoading] = useState(false); - const navigate = useNavigate(); - const isMobile = useIsMobile(); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + const isMobile = useIsMobile(); - useEffect(() => { - if (handleOAuthCallback()) { - message.success('GitHub登录成功!'); - navigate('/'); - return; - } - - if (isAuthenticated()) { - navigate('/'); - } - }, [navigate]); - - const onFinish = async (values: any) => { - setLoading(true); - try { - const response = await login({ - email: values.email, - password: values.password - }); - - if (response.success && response.data) { - // 保存认证信息 - saveAuthData(response.data); + useEffect(() => { + const checkOAuthCallback = async () => { + try { + if (await handleOAuthCallback()) { + message.success('第三方登录成功!'); + navigate('/'); + return; + } + + if (isAuthenticated()) { + navigate('/'); + } + } catch (error) { + console.error('处理登录回调失败:', error); + message.error('登录过程中出现错误'); + } + }; - // 显示成功消息 - message.success(response.message || '登录成功!'); - - // 跳转到首页 - navigate('/'); - } else { - // 显示错误消息 - message.error(response.message || '登录失败,请检查账号和密码'); - } - } catch (error) { - console.error('登录出错:', error); - message.error('登录过程中出现错误,请稍后重试'); - } finally { - setLoading(false); - } - }; + checkOAuthCallback(); + }, [navigate]); - const handleGitHubLogin = () => { - window.location.href = getGitHubLoginUrl(); - }; + const onFinish = async (values: any) => { + setLoading(true); + try { + const response = await login({ + email: values.email, + password: values.password + }); - return ( - - {/* 左侧登录表单 */} - -
-
- - 欢迎回到 Foxel - - - 请登录您的账户以继续使用 - -
+ if (response.success && response.data) { + // 保存认证信息 + saveAuthData(response.data); -
- - } - placeholder="邮箱" - style={{ - height: '50px', - borderRadius: '10px', - backgroundColor: '#f8f9fa', - border: '1px solid #eaeaea' - }} - /> - + + // 显示成功消息 + message.success(response.message || '登录成功!'); - - } - placeholder="密码" - style={{ - height: '50px', - borderRadius: '10px', - backgroundColor: '#f8f9fa', - border: '1px solid #eaeaea' - }} - /> - + // 跳转到首页 + navigate('/'); + } else { + // 显示错误消息 + message.error(response.message || '登录失败,请检查账号和密码'); + } + } catch (error) { + console.error('登录出错:', error); + message.error('登录过程中出现错误,请稍后重试'); + } finally { + setLoading(false); + } + }; - -
- 记住我 - 忘记密码? -
-
+ const handleGitHubLogin = () => { + window.location.href = getGitHubLoginUrl(); + }; - - - + > +
+
+ + 欢迎回到 Foxel + + + 请登录您的账户以继续使用 + +
- - 或使用以下方式登录 - + + + } + placeholder="邮箱" + style={{ + height: '50px', + borderRadius: '10px', + backgroundColor: '#f8f9fa', + border: '1px solid #eaeaea' + }} + /> + -
-
+ + } + placeholder="密码" + style={{ + height: '50px', + borderRadius: '10px', + backgroundColor: '#f8f9fa', + border: '1px solid #eaeaea' + }} + /> + -
- - 还没有账户? 立即注册 - -
- - - -
-
- -
- + +
+ 记住我 + 忘记密码? +
+
- {/* 右侧视觉区域 - 仅在非移动设备上显示 */} - {!isMobile && ( - -
- -
- -
- - 图片管理新方式 - - - Foxel 提供高效、直观的图片管理体验,让您轻松整理、查找和分享珍贵的视觉记忆。 - - - {/* 图片管理界面预览 */} -
-
-
-
-
-
- -
- Foxel 界面预览 -
-
-
- - )} - - ); + + + + + + 或使用以下方式登录 + + +
+
+ +
+ + 还没有账户? 立即注册 + +
+ + + +
+
+ +
+ + + {/* 右侧视觉区域 - 仅在非移动设备上显示 */} + {!isMobile && ( + +
+ +
+ +
+ + 图片管理新方式 + + + Foxel 提供高效、直观的图片管理体验,让您轻松整理、查找和分享珍贵的视觉记忆。 + + + {/* 图片管理界面预览 */} +
+
+
+
+
+
+ +
+ Foxel 界面预览 +
+
+
+ + )} + + ); }; export default Login; \ No newline at end of file diff --git a/View/src/pages/register/Index.tsx b/View/src/pages/register/Index.tsx index d6e856c..3daa09e 100644 --- a/View/src/pages/register/Index.tsx +++ b/View/src/pages/register/Index.tsx @@ -13,16 +13,24 @@ const Register: React.FC = () => { const isMobile = useIsMobile(); useEffect(() => { - // 处理可能的OAuth回调 - if (handleOAuthCallback()) { - message.success('使用GitHub账号注册成功!'); - navigate('/'); - return; - } + const checkOAuthCallback = async () => { + try { + if (await handleOAuthCallback()) { + message.success('使用GitHub账号注册成功!'); + navigate('/'); + return; + } - if (isAuthenticated()) { - navigate('/'); - } + if (isAuthenticated()) { + navigate('/'); + } + } catch (error) { + console.error('处理登录回调失败:', error); + message.error('登录过程中出现错误'); + } + }; + + checkOAuthCallback(); }, [navigate]); const onFinish = async (values: any) => { diff --git a/View/src/pages/settings/SystemConfig.tsx b/View/src/pages/settings/SystemConfig.tsx index 4286945..6a94455 100644 --- a/View/src/pages/settings/SystemConfig.tsx +++ b/View/src/pages/settings/SystemConfig.tsx @@ -138,12 +138,14 @@ const SystemConfig: React.FC = () => { groupName="Authentication" configs={{ "GitHubClientId": configs.Authentication?.["GitHubClientId"] || '', - "GitHubClientSecret": configs.Authentication?.["GitHubClientSecret"] || '' + "GitHubClientSecret": configs.Authentication?.["GitHubClientSecret"] || '', + "GitHubCallbackUrl": configs.Authentication?.["GitHubCallbackUrl"] || '' }} onSave={(_group, key, value) => handleSaveConfig('Authentication', key, value)} descriptions={{ "GitHubClientId": 'GitHub OAuth 应用客户端ID', - "GitHubClientSecret": 'GitHub OAuth 应用客户端密钥' + "GitHubClientSecret": 'GitHub OAuth 应用客户端密钥', + "GitHubCallbackUrl": 'GitHub OAuth 认证回调地址' }} />