mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-24 04:27:27 +08:00
feat(server): client version check (#9205)
Co-authored-by: forehalo <forehalo@gmail.com>
This commit is contained in:
@@ -92,6 +92,7 @@
|
|||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
"semver": "^7.6.3",
|
||||||
"ses": "^1.10.0",
|
"ses": "^1.10.0",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"stripe": "^17.4.0",
|
"stripe": "^17.4.0",
|
||||||
@@ -119,6 +120,7 @@
|
|||||||
"@types/on-headers": "^1.0.3",
|
"@types/on-headers": "^1.0.3",
|
||||||
"@types/react": "^19.0.1",
|
"@types/react": "^19.0.1",
|
||||||
"@types/react-dom": "^19.0.2",
|
"@types/react-dom": "^19.0.2",
|
||||||
|
"@types/semver": "^7.5.8",
|
||||||
"@types/sinon": "^17.0.3",
|
"@types/sinon": "^17.0.3",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"ava": "^6.2.0",
|
"ava": "^6.2.0",
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import test from 'ava';
|
||||||
|
import Sinon from 'sinon';
|
||||||
|
|
||||||
|
import { AppModule } from '../app.module';
|
||||||
|
import { Runtime, UseNamedGuard } from '../base';
|
||||||
|
import { Public } from '../core/auth/guard';
|
||||||
|
import { VersionService } from '../core/version/service';
|
||||||
|
import { createTestingApp, TestingApp } from './utils';
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Controller('/guarded')
|
||||||
|
class GuardedController {
|
||||||
|
@UseNamedGuard('version')
|
||||||
|
@Get('/test')
|
||||||
|
test() {
|
||||||
|
return 'test';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let app: TestingApp;
|
||||||
|
let runtime: Sinon.SinonStubbedInstance<Runtime>;
|
||||||
|
let version: VersionService;
|
||||||
|
|
||||||
|
function checkVersion(enabled = true) {
|
||||||
|
runtime.fetch.withArgs('client/versionControl.enabled').resolves(enabled);
|
||||||
|
|
||||||
|
runtime.fetch
|
||||||
|
.withArgs('client/versionControl.requiredVersion')
|
||||||
|
.resolves('>=0.20.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
test.before(async () => {
|
||||||
|
app = await createTestingApp({
|
||||||
|
imports: [AppModule],
|
||||||
|
controllers: [GuardedController],
|
||||||
|
tapModule: m => {
|
||||||
|
m.overrideProvider(Runtime).useValue(Sinon.createStubInstance(Runtime));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
runtime = app.get(Runtime);
|
||||||
|
version = app.get(VersionService, { strict: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
Sinon.reset();
|
||||||
|
|
||||||
|
checkVersion(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.after.always(async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should passthrough if version check is not enabled', async t => {
|
||||||
|
checkVersion(false);
|
||||||
|
|
||||||
|
const spy = Sinon.spy(version, 'checkVersion');
|
||||||
|
|
||||||
|
let res = await app.GET('/guarded/test');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
|
||||||
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.20.0');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
|
||||||
|
res = await app.GET('/guarded/test').set('x-affine-version', 'invalid');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
t.true(spy.notCalled);
|
||||||
|
spy.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should passthrough is version range is invalid', async t => {
|
||||||
|
runtime.fetch
|
||||||
|
.withArgs('client/versionControl.requiredVersion')
|
||||||
|
.resolves('invalid');
|
||||||
|
|
||||||
|
let res = await app.GET('/guarded/test').set('x-affine-version', 'invalid');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should pass if client version is allowed', async t => {
|
||||||
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.20.0');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
|
||||||
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.21.0');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
|
||||||
|
runtime.fetch
|
||||||
|
.withArgs('client/versionControl.requiredVersion')
|
||||||
|
.resolves('>=0.19.0');
|
||||||
|
|
||||||
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.19.0');
|
||||||
|
|
||||||
|
t.is(res.status, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should fail if client version is not set or invalid', async t => {
|
||||||
|
let res = await app.GET('/guarded/test');
|
||||||
|
|
||||||
|
t.is(res.status, 403);
|
||||||
|
t.is(
|
||||||
|
res.body.message,
|
||||||
|
'Unsupported client with version [unset_or_invalid], required version is [>=0.20.0].'
|
||||||
|
);
|
||||||
|
|
||||||
|
res = await app.GET('/guarded/test').set('x-affine-version', 'invalid');
|
||||||
|
|
||||||
|
t.is(res.status, 403);
|
||||||
|
t.is(
|
||||||
|
res.body.message,
|
||||||
|
'Unsupported client with version [invalid], required version is [>=0.20.0].'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should tell upgrade if client version is lower than allowed', async t => {
|
||||||
|
runtime.fetch
|
||||||
|
.withArgs('client/versionControl.requiredVersion')
|
||||||
|
.resolves('>=0.21.0 <=0.22.0');
|
||||||
|
|
||||||
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.20.0');
|
||||||
|
|
||||||
|
t.is(res.status, 403);
|
||||||
|
t.is(
|
||||||
|
res.body.message,
|
||||||
|
'Unsupported client with version [0.20.0], required version is [>=0.21.0 <=0.22.0].'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should tell downgrade if client version is higher than allowed', async t => {
|
||||||
|
runtime.fetch
|
||||||
|
.withArgs('client/versionControl.requiredVersion')
|
||||||
|
.resolves('>=0.20.0 <=0.22.0');
|
||||||
|
|
||||||
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.23.0');
|
||||||
|
|
||||||
|
t.is(res.status, 403);
|
||||||
|
t.is(
|
||||||
|
res.body.message,
|
||||||
|
'Unsupported client with version [0.23.0], required version is [>=0.20.0 <=0.22.0].'
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -49,6 +49,7 @@ import { SelfhostModule } from './core/selfhost';
|
|||||||
import { StorageModule } from './core/storage';
|
import { StorageModule } from './core/storage';
|
||||||
import { SyncModule } from './core/sync';
|
import { SyncModule } from './core/sync';
|
||||||
import { UserModule } from './core/user';
|
import { UserModule } from './core/user';
|
||||||
|
import { VersionModule } from './core/version';
|
||||||
import { WorkspaceModule } from './core/workspaces';
|
import { WorkspaceModule } from './core/workspaces';
|
||||||
import { ModelsModule } from './models';
|
import { ModelsModule } from './models';
|
||||||
import { REGISTERED_PLUGINS } from './plugins';
|
import { REGISTERED_PLUGINS } from './plugins';
|
||||||
@@ -225,6 +226,7 @@ export function buildAppModule() {
|
|||||||
// graphql server only
|
// graphql server only
|
||||||
.useIf(
|
.useIf(
|
||||||
config => config.flavor.graphql,
|
config => config.flavor.graphql,
|
||||||
|
VersionModule,
|
||||||
GqlModule,
|
GqlModule,
|
||||||
StorageModule,
|
StorageModule,
|
||||||
ServerConfigModule,
|
ServerConfigModule,
|
||||||
|
|||||||
@@ -724,4 +724,15 @@ export const USER_FRIENDLY_ERRORS = {
|
|||||||
message: ({ limit }) =>
|
message: ({ limit }) =>
|
||||||
`You cannot downgrade the workspace from team workspace because there are more than ${limit} members that are currently active.`,
|
`You cannot downgrade the workspace from team workspace because there are more than ${limit} members that are currently active.`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// version errors
|
||||||
|
unsupported_client_version: {
|
||||||
|
type: 'action_forbidden',
|
||||||
|
args: {
|
||||||
|
clientVersion: 'string',
|
||||||
|
requiredVersion: 'string',
|
||||||
|
},
|
||||||
|
message: ({ clientVersion, requiredVersion }) =>
|
||||||
|
`Unsupported client with version [${clientVersion}], required version is [${requiredVersion}].`,
|
||||||
|
},
|
||||||
} satisfies Record<string, UserFriendlyErrorOptions>;
|
} satisfies Record<string, UserFriendlyErrorOptions>;
|
||||||
|
|||||||
@@ -794,6 +794,17 @@ export class WorkspaceMembersExceedLimitToDowngrade extends UserFriendlyError {
|
|||||||
super('bad_request', 'workspace_members_exceed_limit_to_downgrade', message, args);
|
super('bad_request', 'workspace_members_exceed_limit_to_downgrade', message, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ObjectType()
|
||||||
|
class UnsupportedClientVersionDataType {
|
||||||
|
@Field() clientVersion!: string
|
||||||
|
@Field() requiredVersion!: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UnsupportedClientVersion extends UserFriendlyError {
|
||||||
|
constructor(args: UnsupportedClientVersionDataType, message?: string | ((args: UnsupportedClientVersionDataType) => string)) {
|
||||||
|
super('action_forbidden', 'unsupported_client_version', message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
export enum ErrorNames {
|
export enum ErrorNames {
|
||||||
INTERNAL_SERVER_ERROR,
|
INTERNAL_SERVER_ERROR,
|
||||||
TOO_MANY_REQUEST,
|
TOO_MANY_REQUEST,
|
||||||
@@ -895,7 +906,8 @@ export enum ErrorNames {
|
|||||||
LICENSE_NOT_FOUND,
|
LICENSE_NOT_FOUND,
|
||||||
INVALID_LICENSE_TO_ACTIVATE,
|
INVALID_LICENSE_TO_ACTIVATE,
|
||||||
INVALID_LICENSE_UPDATE_PARAMS,
|
INVALID_LICENSE_UPDATE_PARAMS,
|
||||||
WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE
|
WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE,
|
||||||
|
UNSUPPORTED_CLIENT_VERSION
|
||||||
}
|
}
|
||||||
registerEnumType(ErrorNames, {
|
registerEnumType(ErrorNames, {
|
||||||
name: 'ErrorNames'
|
name: 'ErrorNames'
|
||||||
@@ -904,5 +916,5 @@ registerEnumType(ErrorNames, {
|
|||||||
export const ErrorDataUnionType = createUnionType({
|
export const ErrorDataUnionType = createUnionType({
|
||||||
name: 'ErrorDataUnion',
|
name: 'ErrorDataUnion',
|
||||||
types: () =>
|
types: () =>
|
||||||
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocActionDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const,
|
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocActionDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType, UnsupportedClientVersionDataType] as const,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,14 +18,19 @@ export class BasicGuard implements CanActivate {
|
|||||||
|
|
||||||
async canActivate(context: ExecutionContext) {
|
async canActivate(context: ExecutionContext) {
|
||||||
// get registered guard name
|
// get registered guard name
|
||||||
const providerName = this.reflector.get<string>(
|
const providerName = this.reflector.get<string[]>(
|
||||||
BasicGuardSymbol,
|
BasicGuardSymbol,
|
||||||
context.getHandler()
|
context.getHandler()
|
||||||
);
|
);
|
||||||
|
|
||||||
const provider = GUARD_PROVIDER[providerName as NamedGuards];
|
if (Array.isArray(providerName) && providerName.length > 0) {
|
||||||
if (provider) {
|
for (const name of providerName) {
|
||||||
return await provider.canActivate(context);
|
const provider = GUARD_PROVIDER[name as NamedGuards];
|
||||||
|
if (provider) {
|
||||||
|
const ret = await provider.canActivate(context);
|
||||||
|
if (!ret) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -46,5 +51,5 @@ export class BasicGuard implements CanActivate {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const UseNamedGuard = (name: NamedGuards) =>
|
export const UseNamedGuard = (...name: NamedGuards[]) =>
|
||||||
applyDecorators(UseGuards(BasicGuard), SetMetadata(BasicGuardSymbol, name));
|
applyDecorators(UseGuards(BasicGuard), SetMetadata(BasicGuardSymbol, name));
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
|
@UseNamedGuard('version')
|
||||||
@Post('/preflight')
|
@Post('/preflight')
|
||||||
async preflight(
|
async preflight(
|
||||||
@Body() params?: { email: string }
|
@Body() params?: { email: string }
|
||||||
@@ -108,7 +109,7 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
@UseNamedGuard('captcha')
|
@UseNamedGuard('version', 'captcha')
|
||||||
@Post('/sign-in')
|
@Post('/sign-in')
|
||||||
@Header('content-type', 'application/json')
|
@Header('content-type', 'application/json')
|
||||||
async signIn(
|
async signIn(
|
||||||
@@ -260,6 +261,7 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
|
@UseNamedGuard('version')
|
||||||
@Post('/magic-link')
|
@Post('/magic-link')
|
||||||
async magicLinkSignIn(
|
async magicLinkSignIn(
|
||||||
@Req() req: Request,
|
@Req() req: Request,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { defineRuntimeConfig, ModuleConfig } from '../../base/config';
|
||||||
|
|
||||||
|
export interface VersionConfig {
|
||||||
|
versionControl: {
|
||||||
|
enabled: boolean;
|
||||||
|
requiredVersion: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '../../base/config' {
|
||||||
|
interface AppConfig {
|
||||||
|
client: ModuleConfig<never, VersionConfig>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '../../base/guard' {
|
||||||
|
interface RegisterGuardName {
|
||||||
|
version: 'version';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineRuntimeConfig('client', {
|
||||||
|
'versionControl.enabled': {
|
||||||
|
desc: 'Whether check version of client before accessing the server.',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
'versionControl.requiredVersion': {
|
||||||
|
desc: "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.",
|
||||||
|
default: '>=0.20.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import type {
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
OnModuleInit,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getRequestResponseFromContext,
|
||||||
|
GuardProvider,
|
||||||
|
Runtime,
|
||||||
|
} from '../../base';
|
||||||
|
import { VersionService } from './service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class VersionGuardProvider
|
||||||
|
extends GuardProvider
|
||||||
|
implements CanActivate, OnModuleInit
|
||||||
|
{
|
||||||
|
name = 'version' as const;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly runtime: Runtime,
|
||||||
|
private readonly version: VersionService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async canActivate(context: ExecutionContext) {
|
||||||
|
if (!(await this.runtime.fetch('client/versionControl.enabled'))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { req } = getRequestResponseFromContext(context);
|
||||||
|
|
||||||
|
const version = req.headers['x-affine-version'] as string | undefined;
|
||||||
|
|
||||||
|
return this.version.checkVersion(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import './config';
|
||||||
|
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { VersionGuardProvider } from './guard';
|
||||||
|
import { VersionService } from './service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [VersionService, VersionGuardProvider],
|
||||||
|
})
|
||||||
|
export class VersionModule {}
|
||||||
|
|
||||||
|
export type { VersionConfig } from './config';
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import semver from 'semver';
|
||||||
|
|
||||||
|
import { Runtime, UnsupportedClientVersion } from '../../base';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class VersionService {
|
||||||
|
private readonly logger = new Logger(VersionService.name);
|
||||||
|
|
||||||
|
constructor(private readonly runtime: Runtime) {}
|
||||||
|
|
||||||
|
async checkVersion(clientVersion?: string) {
|
||||||
|
const requiredVersion = await this.runtime.fetch(
|
||||||
|
'client/versionControl.requiredVersion'
|
||||||
|
);
|
||||||
|
|
||||||
|
const range = await this.getVersionRange(requiredVersion);
|
||||||
|
if (!range) {
|
||||||
|
// ignore invalid allowed version config
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clientVersion || !semver.satisfies(clientVersion, range)) {
|
||||||
|
throw new UnsupportedClientVersion({
|
||||||
|
clientVersion: clientVersion ?? 'unset_or_invalid',
|
||||||
|
requiredVersion,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly cachedVersionRange = new Map<
|
||||||
|
string,
|
||||||
|
semver.Range | undefined
|
||||||
|
>();
|
||||||
|
private async getVersionRange(versionRange: string) {
|
||||||
|
if (this.cachedVersionRange.has(versionRange)) {
|
||||||
|
return this.cachedVersionRange.get(versionRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
let range: semver.Range | undefined;
|
||||||
|
try {
|
||||||
|
range = new semver.Range(versionRange, { loose: false });
|
||||||
|
if (!semver.validRange(range)) {
|
||||||
|
range = undefined;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
range = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!range) {
|
||||||
|
this.logger.error(`invalid version range: ${versionRange}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cachedVersionRange.set(versionRange, range);
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
OauthAccountAlreadyConnected,
|
OauthAccountAlreadyConnected,
|
||||||
OauthStateExpired,
|
OauthStateExpired,
|
||||||
UnknownOauthProvider,
|
UnknownOauthProvider,
|
||||||
|
UseNamedGuard,
|
||||||
} from '../../base';
|
} from '../../base';
|
||||||
import { AuthService, Public } from '../../core/auth';
|
import { AuthService, Public } from '../../core/auth';
|
||||||
import { Models } from '../../models';
|
import { Models } from '../../models';
|
||||||
@@ -34,6 +35,7 @@ export class OAuthController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
|
@UseNamedGuard('version')
|
||||||
@Post('/preflight')
|
@Post('/preflight')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
async preflight(
|
async preflight(
|
||||||
@@ -63,6 +65,7 @@ export class OAuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
|
@UseNamedGuard('version')
|
||||||
@Post('/callback')
|
@Post('/callback')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
async callback(
|
async callback(
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ type EditorType {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
||||||
|
|
||||||
enum ErrorNames {
|
enum ErrorNames {
|
||||||
ACCESS_DENIED
|
ACCESS_DENIED
|
||||||
@@ -409,6 +409,7 @@ enum ErrorNames {
|
|||||||
TOO_MANY_REQUEST
|
TOO_MANY_REQUEST
|
||||||
UNKNOWN_OAUTH_PROVIDER
|
UNKNOWN_OAUTH_PROVIDER
|
||||||
UNSPLASH_IS_NOT_CONFIGURED
|
UNSPLASH_IS_NOT_CONFIGURED
|
||||||
|
UNSUPPORTED_CLIENT_VERSION
|
||||||
UNSUPPORTED_SUBSCRIPTION_PLAN
|
UNSUPPORTED_SUBSCRIPTION_PLAN
|
||||||
USER_AVATAR_NOT_FOUND
|
USER_AVATAR_NOT_FOUND
|
||||||
USER_NOT_FOUND
|
USER_NOT_FOUND
|
||||||
@@ -1101,6 +1102,11 @@ type UnknownOauthProviderDataType {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UnsupportedClientVersionDataType {
|
||||||
|
clientVersion: String!
|
||||||
|
requiredVersion: String!
|
||||||
|
}
|
||||||
|
|
||||||
type UnsupportedSubscriptionPlanDataType {
|
type UnsupportedSubscriptionPlanDataType {
|
||||||
plan: String!
|
plan: String!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -426,6 +426,7 @@ export type ErrorDataUnion =
|
|||||||
| SubscriptionNotExistsDataType
|
| SubscriptionNotExistsDataType
|
||||||
| SubscriptionPlanNotFoundDataType
|
| SubscriptionPlanNotFoundDataType
|
||||||
| UnknownOauthProviderDataType
|
| UnknownOauthProviderDataType
|
||||||
|
| UnsupportedClientVersionDataType
|
||||||
| UnsupportedSubscriptionPlanDataType
|
| UnsupportedSubscriptionPlanDataType
|
||||||
| VersionRejectedDataType
|
| VersionRejectedDataType
|
||||||
| WorkspaceMembersExceedLimitToDowngradeDataType
|
| WorkspaceMembersExceedLimitToDowngradeDataType
|
||||||
@@ -523,6 +524,7 @@ export enum ErrorNames {
|
|||||||
TOO_MANY_REQUEST = 'TOO_MANY_REQUEST',
|
TOO_MANY_REQUEST = 'TOO_MANY_REQUEST',
|
||||||
UNKNOWN_OAUTH_PROVIDER = 'UNKNOWN_OAUTH_PROVIDER',
|
UNKNOWN_OAUTH_PROVIDER = 'UNKNOWN_OAUTH_PROVIDER',
|
||||||
UNSPLASH_IS_NOT_CONFIGURED = 'UNSPLASH_IS_NOT_CONFIGURED',
|
UNSPLASH_IS_NOT_CONFIGURED = 'UNSPLASH_IS_NOT_CONFIGURED',
|
||||||
|
UNSUPPORTED_CLIENT_VERSION = 'UNSUPPORTED_CLIENT_VERSION',
|
||||||
UNSUPPORTED_SUBSCRIPTION_PLAN = 'UNSUPPORTED_SUBSCRIPTION_PLAN',
|
UNSUPPORTED_SUBSCRIPTION_PLAN = 'UNSUPPORTED_SUBSCRIPTION_PLAN',
|
||||||
USER_AVATAR_NOT_FOUND = 'USER_AVATAR_NOT_FOUND',
|
USER_AVATAR_NOT_FOUND = 'USER_AVATAR_NOT_FOUND',
|
||||||
USER_NOT_FOUND = 'USER_NOT_FOUND',
|
USER_NOT_FOUND = 'USER_NOT_FOUND',
|
||||||
@@ -1534,6 +1536,12 @@ export interface UnknownOauthProviderDataType {
|
|||||||
name: Scalars['String']['output'];
|
name: Scalars['String']['output'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UnsupportedClientVersionDataType {
|
||||||
|
__typename?: 'UnsupportedClientVersionDataType';
|
||||||
|
clientVersion: Scalars['String']['output'];
|
||||||
|
requiredVersion: Scalars['String']['output'];
|
||||||
|
}
|
||||||
|
|
||||||
export interface UnsupportedSubscriptionPlanDataType {
|
export interface UnsupportedSubscriptionPlanDataType {
|
||||||
__typename?: 'UnsupportedSubscriptionPlanDataType';
|
__typename?: 'UnsupportedSubscriptionPlanDataType';
|
||||||
plan: Scalars['String']['output'];
|
plan: Scalars['String']['output'];
|
||||||
|
|||||||
@@ -7503,6 +7503,13 @@ export function useAFFiNEI18N(): {
|
|||||||
["error.WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE"](options: {
|
["error.WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE"](options: {
|
||||||
readonly limit: string;
|
readonly limit: string;
|
||||||
}): string;
|
}): string;
|
||||||
|
/**
|
||||||
|
* `Unsupported client with version [{{clientVersion}}], required version is [{{requiredVersion}}].`
|
||||||
|
*/
|
||||||
|
["error.UNSUPPORTED_CLIENT_VERSION"](options: Readonly<{
|
||||||
|
clientVersion: string;
|
||||||
|
requiredVersion: string;
|
||||||
|
}>): string;
|
||||||
} { const { t } = useTranslation(); return useMemo(() => createProxy((key) => t.bind(null, key)), [t]); }
|
} { const { t } = useTranslation(); return useMemo(() => createProxy((key) => t.bind(null, key)), [t]); }
|
||||||
function createComponent(i18nKey: string) {
|
function createComponent(i18nKey: string) {
|
||||||
return (props) => createElement(Trans, { i18nKey, shouldUnescape: true, ...props });
|
return (props) => createElement(Trans, { i18nKey, shouldUnescape: true, ...props });
|
||||||
|
|||||||
@@ -1843,5 +1843,6 @@
|
|||||||
"error.LICENSE_NOT_FOUND": "License not found.",
|
"error.LICENSE_NOT_FOUND": "License not found.",
|
||||||
"error.INVALID_LICENSE_TO_ACTIVATE": "Invalid license to activate.",
|
"error.INVALID_LICENSE_TO_ACTIVATE": "Invalid license to activate.",
|
||||||
"error.INVALID_LICENSE_UPDATE_PARAMS": "Invalid license update params. {{reason}}",
|
"error.INVALID_LICENSE_UPDATE_PARAMS": "Invalid license update params. {{reason}}",
|
||||||
"error.WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE": "You cannot downgrade the workspace from team workspace because there are more than {{limit}} members that are currently active."
|
"error.WORKSPACE_MEMBERS_EXCEED_LIMIT_TO_DOWNGRADE": "You cannot downgrade the workspace from team workspace because there are more than {{limit}} members that are currently active.",
|
||||||
|
"error.UNSUPPORTED_CLIENT_VERSION": "Unsupported client with version [{{clientVersion}}], required version is [{{requiredVersion}}]."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -831,6 +831,7 @@ __metadata:
|
|||||||
"@types/on-headers": "npm:^1.0.3"
|
"@types/on-headers": "npm:^1.0.3"
|
||||||
"@types/react": "npm:^19.0.1"
|
"@types/react": "npm:^19.0.1"
|
||||||
"@types/react-dom": "npm:^19.0.2"
|
"@types/react-dom": "npm:^19.0.2"
|
||||||
|
"@types/semver": "npm:^7.5.8"
|
||||||
"@types/sinon": "npm:^17.0.3"
|
"@types/sinon": "npm:^17.0.3"
|
||||||
"@types/supertest": "npm:^6.0.2"
|
"@types/supertest": "npm:^6.0.2"
|
||||||
ava: "npm:^6.2.0"
|
ava: "npm:^6.2.0"
|
||||||
@@ -870,6 +871,7 @@ __metadata:
|
|||||||
react-email: "npm:3.0.7"
|
react-email: "npm:3.0.7"
|
||||||
reflect-metadata: "npm:^0.2.2"
|
reflect-metadata: "npm:^0.2.2"
|
||||||
rxjs: "npm:^7.8.1"
|
rxjs: "npm:^7.8.1"
|
||||||
|
semver: "npm:^7.6.3"
|
||||||
ses: "npm:^1.10.0"
|
ses: "npm:^1.10.0"
|
||||||
sinon: "npm:^19.0.2"
|
sinon: "npm:^19.0.2"
|
||||||
socket.io: "npm:^4.8.1"
|
socket.io: "npm:^4.8.1"
|
||||||
@@ -15527,6 +15529,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/semver@npm:^7.5.8":
|
||||||
|
version: 7.5.8
|
||||||
|
resolution: "@types/semver@npm:7.5.8"
|
||||||
|
checksum: 10/3496808818ddb36deabfe4974fd343a78101fa242c4690044ccdc3b95dcf8785b494f5d628f2f47f38a702f8db9c53c67f47d7818f2be1b79f2efb09692e1178
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/send@npm:*":
|
"@types/send@npm:*":
|
||||||
version: 0.17.4
|
version: 0.17.4
|
||||||
resolution: "@types/send@npm:0.17.4"
|
resolution: "@types/send@npm:0.17.4"
|
||||||
|
|||||||
Reference in New Issue
Block a user