mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 12:06:35 +08:00
feat(server): introduce user friendly server errors (#7111)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Config, URLHelper } from '../../../fundamentals';
|
||||
import { OAuthProviderName } from '../config';
|
||||
@@ -39,74 +39,60 @@ export class GithubOAuthProvider extends AutoRegisteredOAuthProvider {
|
||||
}
|
||||
|
||||
async getToken(code: string) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://github.com/login/oauth/access_token',
|
||||
{
|
||||
method: 'POST',
|
||||
body: this.url.stringify({
|
||||
code,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret,
|
||||
redirect_uri: this.url.link('/oauth/callback'),
|
||||
}),
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const ghToken = (await response.json()) as AuthTokenResponse;
|
||||
|
||||
return {
|
||||
accessToken: ghToken.access_token,
|
||||
scope: ghToken.scope,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
}, ${JSON.stringify(await response.json())}`
|
||||
);
|
||||
const response = await fetch(
|
||||
'https://github.com/login/oauth/access_token',
|
||||
{
|
||||
method: 'POST',
|
||||
body: this.url.stringify({
|
||||
code,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret,
|
||||
redirect_uri: this.url.link('/oauth/callback'),
|
||||
}),
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
`Failed to get access_token, err: ${(e as Error).message}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const ghToken = (await response.json()) as AuthTokenResponse;
|
||||
|
||||
return {
|
||||
accessToken: ghToken.access_token,
|
||||
scope: ghToken.scope,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
}, ${JSON.stringify(await response.json())}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getUser(token: string) {
|
||||
try {
|
||||
const response = await fetch('https://api.github.com/user', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
const response = await fetch('https://api.github.com/user', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const user = (await response.json()) as UserInfo;
|
||||
if (response.ok) {
|
||||
const user = (await response.json()) as UserInfo;
|
||||
|
||||
return {
|
||||
id: user.login,
|
||||
avatarUrl: user.avatar_url,
|
||||
email: user.email,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
} ${await response.text()}`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
`Failed to get user information, err: ${(e as Error).stack}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
return {
|
||||
id: user.login,
|
||||
avatarUrl: user.avatar_url,
|
||||
email: user.email,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
} ${await response.text()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Config, URLHelper } from '../../../fundamentals';
|
||||
import { OAuthProviderName } from '../config';
|
||||
@@ -44,77 +44,63 @@ export class GoogleOAuthProvider extends AutoRegisteredOAuthProvider {
|
||||
}
|
||||
|
||||
async getToken(code: string) {
|
||||
try {
|
||||
const response = await fetch('https://oauth2.googleapis.com/token', {
|
||||
method: 'POST',
|
||||
body: this.url.stringify({
|
||||
code,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret,
|
||||
redirect_uri: this.url.link('/oauth/callback'),
|
||||
grant_type: 'authorization_code',
|
||||
}),
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
const response = await fetch('https://oauth2.googleapis.com/token', {
|
||||
method: 'POST',
|
||||
body: this.url.stringify({
|
||||
code,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret,
|
||||
redirect_uri: this.url.link('/oauth/callback'),
|
||||
grant_type: 'authorization_code',
|
||||
}),
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const ghToken = (await response.json()) as GoogleOAuthTokenResponse;
|
||||
if (response.ok) {
|
||||
const ghToken = (await response.json()) as GoogleOAuthTokenResponse;
|
||||
|
||||
return {
|
||||
accessToken: ghToken.access_token,
|
||||
refreshToken: ghToken.refresh_token,
|
||||
expiresAt: new Date(Date.now() + ghToken.expires_in * 1000),
|
||||
scope: ghToken.scope,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
}, ${JSON.stringify(await response.json())}`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
`Failed to get access_token, err: ${(e as Error).message}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
return {
|
||||
accessToken: ghToken.access_token,
|
||||
refreshToken: ghToken.refresh_token,
|
||||
expiresAt: new Date(Date.now() + ghToken.expires_in * 1000),
|
||||
scope: ghToken.scope,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
}, ${JSON.stringify(await response.json())}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getUser(token: string) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://www.googleapis.com/oauth2/v2/userinfo',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const user = (await response.json()) as UserInfo;
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
avatarUrl: user.picture,
|
||||
email: user.email,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
} ${await response.text()}`
|
||||
);
|
||||
const response = await fetch(
|
||||
'https://www.googleapis.com/oauth2/v2/userinfo',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
`Failed to get user information, err: ${(e as Error).stack}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const user = (await response.json()) as UserInfo;
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
avatarUrl: user.picture,
|
||||
email: user.email,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
} ${await response.text()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
OnModuleInit,
|
||||
} from '@nestjs/common';
|
||||
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, URLHelper } from '../../../fundamentals';
|
||||
@@ -44,6 +39,8 @@ const OIDCConfigurationSchema = z.object({
|
||||
|
||||
type OIDCConfiguration = z.infer<typeof OIDCConfigurationSchema>;
|
||||
|
||||
const logger = new Logger('OIDCClient');
|
||||
|
||||
class OIDCClient {
|
||||
private static async fetch<T = any>(
|
||||
url: string,
|
||||
@@ -53,17 +50,8 @@ class OIDCClient {
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status >= 400 && response.status < 500) {
|
||||
throw new BadRequestException(`Invalid OIDC configuration`, {
|
||||
cause: await response.json(),
|
||||
description: response.statusText,
|
||||
});
|
||||
} else {
|
||||
throw new InternalServerErrorException(`Failed to configure client`, {
|
||||
cause: await response.json(),
|
||||
description: response.statusText,
|
||||
});
|
||||
}
|
||||
logger.error('Failed to fetch OIDC configuration', await response.json());
|
||||
throw new Error(`Failed to configure client`);
|
||||
}
|
||||
const data = await response.json();
|
||||
return verifier.parse(data);
|
||||
|
||||
Reference in New Issue
Block a user