Skip to content

Commit

Permalink
Merge branch 'canary' into darksky/mutate-db-with-transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
darkskygit committed Mar 14, 2024
2 parents 63d0408 + d2bad68 commit 40580a8
Show file tree
Hide file tree
Showing 147 changed files with 1,206 additions and 986 deletions.
6 changes: 6 additions & 0 deletions packages/backend/server/src/core/features/management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,10 @@ export class FeatureManagementService {
async listFeatureWorkspaces(feature: FeatureType) {
return this.feature.listFeatureWorkspaces(feature);
}

async getUserFeatures(userId: string): Promise<FeatureType[]> {
return (await this.feature.getUserFeatures(userId)).map(
f => f.feature.name
);
}
}
11 changes: 10 additions & 1 deletion packages/backend/server/src/core/user/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import { CurrentUser } from '../auth/current-user';
import { Public } from '../auth/guard';
import { sessionUser } from '../auth/service';
import { FeatureManagementService } from '../features';
import { FeatureManagementService, FeatureType } from '../features';
import { QuotaService } from '../quota';
import { AvatarStorage } from '../storage';
import { UserService } from './service';
Expand Down Expand Up @@ -108,6 +108,15 @@ export class UserResolver {
});
}

@Throttle({ default: { limit: 10, ttl: 60 } })
@ResolveField(() => [FeatureType], {
name: 'features',
description: 'Enabled features of a user',
})
async userFeatures(@CurrentUser() user: CurrentUser) {
return this.feature.getUserFeatures(user.id);
}

@Throttle({
default: {
limit: 10,
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/server/src/core/workspaces/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class WorkspacesController {
// get workspace blob
//
// NOTE: because graphql can't represent a File, so we have to use REST API to get blob
@Public()
@Get('/:id/blobs/:name')
@CallTimer('controllers', 'workspace_get_blob')
async blob(
Expand Down Expand Up @@ -61,8 +62,8 @@ export class WorkspacesController {
}

// get doc binary
@Get('/:id/docs/:guid')
@Public()
@Get('/:id/docs/:guid')
@CallTimer('controllers', 'workspace_get_doc')
async doc(
@CurrentUser() user: CurrentUser | undefined,
Expand Down
39 changes: 39 additions & 0 deletions packages/backend/server/src/data/migrations/1710319359062-oauth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { PrismaClient } from '@prisma/client';

import { loop } from './utils/loop';

export class Oauth1710319359062 {
// do the migration
static async up(db: PrismaClient) {
await loop(async (skip, take) => {
const oldRecords = await db.deprecatedNextAuthAccount.findMany({
skip,
take,
orderBy: {
providerAccountId: 'asc',
},
});

await db.connectedAccount.createMany({
data: oldRecords.map(record => ({
userId: record.userId,
provider: record.provider,
scope: record.scope,
providerAccountId: record.providerAccountId,
accessToken: record.access_token,
refreshToken: record.refresh_token,
expiresAt: record.expires_at
? new Date(record.expires_at * 1000)
: null,
})),
});

return oldRecords.length;
}, 10);
}

// revert the migration
static async down(db: PrismaClient) {
await db.connectedAccount.deleteMany({});
}
}
13 changes: 13 additions & 0 deletions packages/backend/server/src/data/migrations/utils/loop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export async function loop(
batchFn: (skip: number, take: number) => Promise<number>,
chunkSize: number = 100
) {
let turn = 0;
let last = chunkSize;

while (last === chunkSize) {
last = await batchFn(chunkSize * turn, chunkSize);

turn++;
}
}
2 changes: 1 addition & 1 deletion packages/backend/server/src/fundamentals/mailer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { MAILER } from './mailer';
@OptionalModule({
providers: [MAILER],
exports: [MAILER],
requires: ['mailer.auth.user', 'mailer.auth.pass'],
requires: ['mailer.auth.user'],
})
class MailerModule {}

Expand Down
16 changes: 14 additions & 2 deletions packages/backend/server/src/fundamentals/mailer/mailer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FactoryProvider } from '@nestjs/common';
import { FactoryProvider, Logger } from '@nestjs/common';
import { createTransport, Transporter } from 'nodemailer';
import SMTPTransport from 'nodemailer/lib/smtp-transport';

Expand All @@ -15,7 +15,19 @@ export const MAILER: FactoryProvider<
> = {
provide: MAILER_SERVICE,
useFactory: (config: Config) => {
return config.mailer ? createTransport(config.mailer) : undefined;
if (config.mailer) {
const logger = new Logger('Mailer');
const auth = config.mailer.auth;
if (auth && auth.user && !('pass' in auth)) {
logger.warn(
'Mailer service has not configured password, please make sure your mailer service allow empty password.'
);
}

return createTransport(config.mailer);
} else {
return undefined;
}
},
inject: [Config],
};
9 changes: 9 additions & 0 deletions packages/backend/server/src/plugins/oauth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,17 @@ export class OAuthController {
}

await this.user.fulfillUser(externalAccount.email, {
emailVerifiedAt: new Date(),
registered: true,
});
await this.db.connectedAccount.create({
data: {
userId: user.id,
provider,
providerAccountId: externalAccount.id,
...tokens,
},
});

return user;
} else {
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/server/src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ type UserType {
"""User email verified"""
emailVerified: Boolean!

"""Enabled features of a user"""
features: [FeatureType!]!

"""User password has been set"""
hasPassword: Boolean
id: ID!
Expand Down
4 changes: 2 additions & 2 deletions packages/common/env/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"private": true,
"type": "module",
"devDependencies": {
"@blocksuite/global": "0.13.0-canary-202403050653-934469c",
"@blocksuite/store": "0.13.0-canary-202403050653-934469c",
"@blocksuite/global": "0.13.0-canary-202403140320-a2b362b",
"@blocksuite/store": "0.13.0-canary-202403140320-a2b362b",
"react": "18.2.0",
"react-dom": "18.2.0",
"vitest": "1.3.1"
Expand Down
8 changes: 4 additions & 4 deletions packages/common/env/src/constant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file should has not side effect
import type { Workspace } from '@blocksuite/store';
import type { DocCollection } from '@blocksuite/store';

declare global {
interface Window {
Expand Down Expand Up @@ -95,12 +95,12 @@ export const Messages = {
};

export class PageNotFoundError extends TypeError {
readonly workspace: Workspace;
readonly docCollection: DocCollection;
readonly pageId: string;

constructor(workspace: Workspace, pageId: string) {
constructor(docCollection: DocCollection, pageId: string) {
super();
this.workspace = workspace;
this.docCollection = docCollection;
this.pageId = pageId;
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/common/env/src/filter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Workspace } from '@blocksuite/store';
import type { DocCollection } from '@blocksuite/store';
import { z } from 'zod';

export const literalValueSchema: z.ZodType<LiteralValue, z.ZodTypeDef> =
Expand Down Expand Up @@ -81,4 +81,4 @@ export const tagSchema = z.object({
});
export type Tag = z.input<typeof tagSchema>;

export type PropertiesMeta = Workspace['meta']['properties'];
export type PropertiesMeta = DocCollection['meta']['properties'];
19 changes: 0 additions & 19 deletions packages/common/env/src/global.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
/// <reference types="@blocksuite/global" />
import { assertEquals } from '@blocksuite/global/utils';
import type { Workspace } from '@blocksuite/store';
import { z } from 'zod';

import { isDesktop, isServer } from './constant.js';
import { UaHelper } from './ua-helper.js';

export const blockSuiteFeatureFlags = z.object({
enable_synced_doc_block: z.boolean(),
enable_expand_database_block: z.boolean(),
enable_bultin_ledits: z.boolean(),
});

export const runtimeFlagsSchema = z.object({
enableTestProperties: z.boolean(),
enableBroadcastChannelProvider: z.boolean(),
Expand All @@ -36,7 +29,6 @@ export const runtimeFlagsSchema = z.object({
// this is for the electron app
serverUrlPrefix: z.string(),
enableMoveDatabase: z.boolean(),
editorFlags: blockSuiteFeatureFlags,
appVersion: z.string(),
editorVersion: z.string(),
appBuildType: z.union([
Expand All @@ -48,8 +40,6 @@ export const runtimeFlagsSchema = z.object({
isSelfHosted: z.boolean().optional(),
});

export type BlockSuiteFeatureFlags = z.infer<typeof blockSuiteFeatureFlags>;

export type RuntimeConfig = z.infer<typeof runtimeFlagsSchema>;

type BrowserBase = {
Expand Down Expand Up @@ -153,12 +143,3 @@ export function setupGlobal() {

globalThis.$AFFINE_SETUP = true;
}

export function setupEditorFlags(workspace: Workspace) {
Object.entries(runtimeConfig.editorFlags).forEach(([key, value]) => {
workspace.awarenessStore.setFlag(
key as keyof BlockSuiteFeatureFlags,
value
);
});
}
10 changes: 5 additions & 5 deletions packages/common/infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"@affine/debug": "workspace:*",
"@affine/env": "workspace:*",
"@affine/templates": "workspace:*",
"@blocksuite/blocks": "0.13.0-canary-202403050653-934469c",
"@blocksuite/global": "0.13.0-canary-202403050653-934469c",
"@blocksuite/store": "0.13.0-canary-202403050653-934469c",
"@blocksuite/blocks": "0.13.0-canary-202403140320-a2b362b",
"@blocksuite/global": "0.13.0-canary-202403140320-a2b362b",
"@blocksuite/store": "0.13.0-canary-202403140320-a2b362b",
"foxact": "^0.2.31",
"jotai": "^2.6.5",
"jotai-effect": "^0.6.0",
Expand All @@ -33,8 +33,8 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine/templates": "workspace:*",
"@blocksuite/lit": "0.13.0-canary-202403050653-934469c",
"@blocksuite/presets": "0.13.0-canary-202403050653-934469c",
"@blocksuite/lit": "0.13.0-canary-202403140320-a2b362b",
"@blocksuite/presets": "0.13.0-canary-202403140320-a2b362b",
"@testing-library/react": "^14.2.1",
"async-call-rpc": "^6.4.0",
"react": "^18.2.0",
Expand Down
31 changes: 30 additions & 1 deletion packages/common/infra/src/atom/settings.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { DebugLogger } from '@affine/debug';
import { setupGlobal } from '@affine/env/global';
import type { DocCollection } from '@blocksuite/store';
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import { atomEffect } from 'jotai-effect';

import { getCurrentStore } from './root-store';

setupGlobal();

const logger = new DebugLogger('affine:settings');

export type DateFormats =
| 'MM/dd/YYYY'
| 'dd/MM/YYYY'
Expand All @@ -25,6 +31,8 @@ export type AppSetting = {
enableNoisyBackground: boolean;
autoCheckUpdate: boolean;
autoDownloadUpdate: boolean;
enableMultiView: boolean;
editorFlags: Partial<Omit<BlockSuiteFlags, 'readonly'>>;
};
export const windowFrameStyleOptions: AppSetting['windowFrameStyle'][] = [
'frameless',
Expand Down Expand Up @@ -63,15 +71,36 @@ const appSettingBaseAtom = atomWithStorage<AppSetting>('affine-settings', {
enableNoisyBackground: true,
autoCheckUpdate: true,
autoDownloadUpdate: true,
enableMultiView: false,
editorFlags: {},
});

export function setupEditorFlags(docCollection: DocCollection) {
const store = getCurrentStore();
const syncEditorFlags = () => {
try {
const editorFlags = getCurrentStore().get(appSettingBaseAtom).editorFlags;
Object.entries(editorFlags).forEach(([key, value]) => {
docCollection.awarenessStore.setFlag(
key as keyof BlockSuiteFlags,
value
);
});
} catch (err) {
logger.error('syncEditorFlags', err);
}
};
store.sub(appSettingBaseAtom, syncEditorFlags);
syncEditorFlags();
}

type SetStateAction<Value> = Value | ((prev: Value) => Value);

const appSettingEffect = atomEffect(get => {
const settings = get(appSettingBaseAtom);
// some values in settings should be synced into electron side
if (environment.isDesktop) {
console.log('set config', settings);
logger.debug('sync settings to electron', settings);
// this api type in @affine/electron-api, but it is circular dependency this package, use any here
(window as any).apis?.updater
.setConfig({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function migratePages(
console.error(e);
}
});
schema.upgradeWorkspace(rootDoc);
schema.upgradeCollection(rootDoc);

// Hard code to upgrade page version to 2.
// Let e2e to ensure the data version is correct.
Expand Down
Loading

0 comments on commit 40580a8

Please sign in to comment.