mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 09:06:19 +08:00
feat: add workspace level feature apis (#5503)
This commit is contained in:
@@ -40,15 +40,6 @@ export class EarlyAccessFeatureConfig extends FeatureConfig {
|
||||
throw new Error('Invalid feature config: type is not EarlyAccess');
|
||||
}
|
||||
}
|
||||
|
||||
checkWhiteList(email: string) {
|
||||
for (const domain in this.config.configs.whitelist) {
|
||||
if (email.endsWith(domain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const FeatureConfigMap = {
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { Config } from '../../config';
|
||||
import { PrismaService } from '../../prisma';
|
||||
import { EarlyAccessFeatureConfig } from './feature';
|
||||
import { FeatureService } from './service';
|
||||
import { FeatureType } from './types';
|
||||
|
||||
enum NewFeaturesKind {
|
||||
EarlyAccess,
|
||||
}
|
||||
const STAFF = ['@toeverything.info'];
|
||||
|
||||
@Injectable()
|
||||
export class FeatureManagementService implements OnModuleInit {
|
||||
export class FeatureManagementService {
|
||||
protected logger = new Logger(FeatureManagementService.name);
|
||||
private earlyAccessFeature?: EarlyAccessFeatureConfig;
|
||||
|
||||
constructor(
|
||||
private readonly feature: FeatureService,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly config: Config
|
||||
) {}
|
||||
async onModuleInit() {
|
||||
this.earlyAccessFeature = await this.feature.getFeature(
|
||||
FeatureType.EarlyAccess
|
||||
);
|
||||
}
|
||||
|
||||
// ======== Admin ========
|
||||
|
||||
// todo(@darkskygit): replace this with abac
|
||||
isStaff(email: string) {
|
||||
return this.earlyAccessFeature?.checkWhiteList(email) ?? false;
|
||||
for (const domain of STAFF) {
|
||||
if (email.endsWith(domain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ======== Early Access ========
|
||||
@@ -38,7 +35,7 @@ export class FeatureManagementService implements OnModuleInit {
|
||||
return this.feature.addUserFeature(
|
||||
userId,
|
||||
FeatureType.EarlyAccess,
|
||||
1,
|
||||
2,
|
||||
'Early access user'
|
||||
);
|
||||
}
|
||||
@@ -63,23 +60,8 @@ export class FeatureManagementService implements OnModuleInit {
|
||||
const canEarlyAccess = await this.feature
|
||||
.hasUserFeature(user.id, FeatureType.EarlyAccess)
|
||||
.catch(() => false);
|
||||
if (canEarlyAccess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Outdated, switch to feature gates
|
||||
const oldCanEarlyAccess = await this.prisma.newFeaturesWaitingList
|
||||
.findUnique({
|
||||
where: { email, type: NewFeaturesKind.EarlyAccess },
|
||||
})
|
||||
.then(x => !!x)
|
||||
.catch(() => false);
|
||||
if (oldCanEarlyAccess) {
|
||||
this.logger.warn(
|
||||
`User ${email} has early access in old table but not in new table`
|
||||
);
|
||||
}
|
||||
return oldCanEarlyAccess;
|
||||
return canEarlyAccess;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
|
||||
@@ -292,9 +292,7 @@ export class FeatureService {
|
||||
return configs.filter(feature => !!feature.feature);
|
||||
}
|
||||
|
||||
async listFeatureWorkspaces(
|
||||
feature: FeatureType
|
||||
): Promise<Omit<WorkspaceType, 'members'>[]> {
|
||||
async listFeatureWorkspaces(feature: FeatureType): Promise<WorkspaceType[]> {
|
||||
return this.prisma.workspaceFeatures
|
||||
.findMany({
|
||||
where: {
|
||||
@@ -314,7 +312,7 @@ export class FeatureService {
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(wss => wss.map(ws => ws.workspace));
|
||||
.then(wss => wss.map(ws => ws.workspace as WorkspaceType));
|
||||
}
|
||||
|
||||
async hasWorkspaceFeature(workspaceId: string, feature: FeatureType) {
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
export enum FeatureType {
|
||||
Copilot = 'copilot',
|
||||
EarlyAccess = 'early_access',
|
||||
}
|
||||
|
||||
registerEnumType(FeatureType, {
|
||||
name: 'FeatureType',
|
||||
description: 'The type of workspace feature',
|
||||
});
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
import { URL } from 'node:url';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { FeatureType } from './common';
|
||||
|
||||
function checkHostname(host: string) {
|
||||
try {
|
||||
return new URL(`https://${host}`).hostname === host;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const featureEarlyAccess = z.object({
|
||||
feature: z.literal(FeatureType.EarlyAccess),
|
||||
configs: z.object({
|
||||
whitelist: z
|
||||
.string()
|
||||
.startsWith('@')
|
||||
.refine(domain => checkHostname(domain.slice(1)))
|
||||
.array(),
|
||||
}),
|
||||
configs: z.object({}),
|
||||
});
|
||||
|
||||
@@ -37,6 +37,12 @@ export const Features: Feature[] = [
|
||||
whitelist: ['@toeverything.info'],
|
||||
},
|
||||
},
|
||||
{
|
||||
feature: FeatureType.EarlyAccess,
|
||||
type: FeatureKind.Feature,
|
||||
version: 2,
|
||||
configs: {},
|
||||
},
|
||||
];
|
||||
|
||||
/// ======== schema infer ========
|
||||
|
||||
Reference in New Issue
Block a user