feat: improve selfhosted login (#14502)

fix #13397
fix #14011

#### PR Dependency Tree


* **PR #14502** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Centralized CORS policy with dynamic origin validation applied to
server and realtime connections
* Improved sign-in flows with contextual, localized error hints and
toast notifications
* Centralized network-error normalization and conditional OAuth provider
fetching

* **Bug Fixes**
* Better feedback for self-hosted connection failures and clearer
authentication error handling
* More robust handling of network-related failures with user-friendly
messages
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-02-23 21:23:01 +08:00
committed by GitHub
parent 6d805b302c
commit 91c5869053
7 changed files with 294 additions and 41 deletions
+27 -4
View File
@@ -5,9 +5,14 @@ import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import {
AFFiNELogger,
buildCorsAllowedOrigins,
CacheInterceptor,
CloudThrottlerGuard,
Config,
CORS_ALLOWED_HEADERS,
CORS_ALLOWED_METHODS,
CORS_EXPOSED_HEADERS,
corsOriginCallback,
GlobalExceptionFilter,
URLHelper,
} from './base';
@@ -16,12 +21,11 @@ import { AuthGuard } from './core/auth';
import { serverTimingAndCache } from './middleware/timing';
const OneMB = 1024 * 1024;
export async function run() {
const { AppModule } = await import('./app.module');
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
cors: true,
cors: false,
rawBody: true,
bodyParser: true,
bufferLogs: true,
@@ -32,6 +36,27 @@ export async function run() {
const logger = app.get(AFFiNELogger);
app.useLogger(logger);
const config = app.get(Config);
const url = app.get(URLHelper);
const allowedOrigins = buildCorsAllowedOrigins(url);
app.enableCors({
origin: (origin, callback) => {
corsOriginCallback(
origin,
allowedOrigins,
blockedOrigin =>
logger.warn(`Blocked CORS request from origin: ${blockedOrigin}`),
callback
);
},
credentials: true,
methods: CORS_ALLOWED_METHODS,
allowedHeaders: CORS_ALLOWED_HEADERS,
exposedHeaders: CORS_EXPOSED_HEADERS,
maxAge: 86400,
optionsSuccessStatus: 204,
});
if (config.server.path) {
app.setGlobalPrefix(config.server.path);
@@ -74,8 +99,6 @@ export async function run() {
});
}
const url = app.get(URLHelper);
await app.listen(config.server.port, config.server.listenAddr);
const formattedAddr = config.server.listenAddr.includes(':')