mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
refactor(server): rename settings to user-settings (#11161)
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "settings" (
|
||||
CREATE TABLE "user_settings" (
|
||||
"user_id" VARCHAR NOT NULL,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
"payload" JSONB NOT NULL,
|
||||
|
||||
CONSTRAINT "settings_pkey" PRIMARY KEY ("user_id")
|
||||
CONSTRAINT "user_settings_pkey" PRIMARY KEY ("user_id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "settings" ADD CONSTRAINT "settings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "user_settings" ADD CONSTRAINT "user_settings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -40,7 +40,7 @@ model User {
|
||||
createdAiJobs AiJobs[] @relation("createdAiJobs")
|
||||
// receive notifications
|
||||
notifications Notification[] @relation("user_notifications")
|
||||
settings Settings?
|
||||
settings UserSettings?
|
||||
|
||||
@@index([email])
|
||||
@@map("users")
|
||||
@@ -747,7 +747,7 @@ model Notification {
|
||||
@@map("notifications")
|
||||
}
|
||||
|
||||
model Settings {
|
||||
model UserSettings {
|
||||
userId String @id @map("user_id") @db.VarChar
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
@@ -755,5 +755,5 @@ model Settings {
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("settings")
|
||||
@@map("user_settings")
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ export * from './blobs';
|
||||
export * from './invite';
|
||||
export * from './notification';
|
||||
export * from './permission';
|
||||
export * from './settings';
|
||||
export * from './testing-app';
|
||||
export * from './testing-module';
|
||||
export * from './user';
|
||||
export * from './user-settings';
|
||||
export * from './utils';
|
||||
export * from './workspace';
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import type { SettingsType, UpdateSettingsInput } from '../../core/user/types';
|
||||
import type {
|
||||
UpdateUserSettingsInput,
|
||||
UserSettingsType,
|
||||
} from '../../core/user/types';
|
||||
import type { TestingApp } from './testing-app';
|
||||
|
||||
export async function getSettings(app: TestingApp): Promise<SettingsType> {
|
||||
export async function getUserSettings(
|
||||
app: TestingApp
|
||||
): Promise<UserSettingsType> {
|
||||
const res = await app.gql(
|
||||
`
|
||||
query settings {
|
||||
@@ -17,13 +22,13 @@ export async function getSettings(app: TestingApp): Promise<SettingsType> {
|
||||
return res.currentUser.settings;
|
||||
}
|
||||
|
||||
export async function updateSettings(
|
||||
export async function updateUserSettings(
|
||||
app: TestingApp,
|
||||
input: UpdateSettingsInput
|
||||
input: UpdateUserSettingsInput
|
||||
): Promise<boolean> {
|
||||
const res = await app.gql(
|
||||
`
|
||||
mutation updateSettings($input: UpdateSettingsInput!) {
|
||||
mutation updateUserSettings($input: UpdateUserSettingsInput!) {
|
||||
updateSettings(input: $input)
|
||||
}
|
||||
`,
|
||||
@@ -89,7 +89,7 @@ test('should create invitation notification and email', async t => {
|
||||
test('should not send invitation email if user setting is not to receive invitation email', async t => {
|
||||
const { notificationService } = t.context;
|
||||
const inviteId = randomUUID();
|
||||
await t.context.models.settings.set(member.id, {
|
||||
await t.context.models.userSettings.set(member.id, {
|
||||
receiveInvitationEmail: false,
|
||||
});
|
||||
const invitationMailCount = t.context.module.mails.count('MemberInvitation');
|
||||
@@ -150,7 +150,7 @@ test('should not send invitation accepted email if user settings is not receive
|
||||
const { notificationService } = t.context;
|
||||
const inviteId = randomUUID();
|
||||
// should not send email if user settings is not receive invitation email
|
||||
await t.context.models.settings.set(owner.id, {
|
||||
await t.context.models.userSettings.set(owner.id, {
|
||||
receiveInvitationEmail: false,
|
||||
});
|
||||
const invitationAcceptedMailCount =
|
||||
@@ -560,7 +560,7 @@ test('should send mention email by user setting', async t => {
|
||||
|
||||
// update user setting to not receive mention email
|
||||
const mentionMailCount = t.context.module.mails.count('Mention');
|
||||
await t.context.models.settings.set(member.id, {
|
||||
await t.context.models.userSettings.set(member.id, {
|
||||
receiveMentionEmail: false,
|
||||
});
|
||||
await notificationService.createMention({
|
||||
|
||||
@@ -45,7 +45,7 @@ export class NotificationService {
|
||||
}
|
||||
|
||||
private async sendMentionEmail(input: MentionNotificationCreate) {
|
||||
const userSetting = await this.models.settings.get(input.userId);
|
||||
const userSetting = await this.models.userSettings.get(input.userId);
|
||||
if (!userSetting.receiveMentionEmail) {
|
||||
return;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ export class NotificationService {
|
||||
// make it easier to test in dev mode
|
||||
this.logger.debug(`Invite link: ${inviteUrl}`);
|
||||
}
|
||||
const userSetting = await this.models.settings.get(input.userId);
|
||||
const userSetting = await this.models.userSettings.get(input.userId);
|
||||
if (!userSetting.receiveInvitationEmail) {
|
||||
return;
|
||||
}
|
||||
@@ -151,7 +151,7 @@ export class NotificationService {
|
||||
const inviterUserId = input.userId;
|
||||
const inviteeUserId = input.body.createdByUserId;
|
||||
const workspaceId = input.body.workspaceId;
|
||||
const userSetting = await this.models.settings.get(inviterUserId);
|
||||
const userSetting = await this.models.userSettings.get(inviterUserId);
|
||||
if (!userSetting.receiveInvitationEmail) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import test from 'ava';
|
||||
|
||||
import {
|
||||
createTestingApp,
|
||||
getSettings,
|
||||
getUserSettings,
|
||||
TestingApp,
|
||||
updateSettings,
|
||||
updateUserSettings,
|
||||
} from '../../../__tests__/utils';
|
||||
|
||||
let app: TestingApp;
|
||||
@@ -19,7 +19,7 @@ test.after.always(async () => {
|
||||
|
||||
test('should get user settings', async t => {
|
||||
await app.signup();
|
||||
const settings = await getSettings(app);
|
||||
const settings = await getUserSettings(app);
|
||||
t.deepEqual(settings, {
|
||||
receiveInvitationEmail: true,
|
||||
receiveMentionEmail: true,
|
||||
@@ -28,30 +28,30 @@ test('should get user settings', async t => {
|
||||
|
||||
test('should update user settings', async t => {
|
||||
await app.signup();
|
||||
await updateSettings(app, {
|
||||
await updateUserSettings(app, {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: false,
|
||||
});
|
||||
const settings = await getSettings(app);
|
||||
const settings = await getUserSettings(app);
|
||||
t.deepEqual(settings, {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: false,
|
||||
});
|
||||
|
||||
await updateSettings(app, {
|
||||
await updateUserSettings(app, {
|
||||
receiveMentionEmail: true,
|
||||
});
|
||||
const settings2 = await getSettings(app);
|
||||
const settings2 = await getUserSettings(app);
|
||||
t.deepEqual(settings2, {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: true,
|
||||
});
|
||||
|
||||
await updateSettings(app, {
|
||||
await updateUserSettings(app, {
|
||||
// ignore undefined value
|
||||
receiveInvitationEmail: undefined,
|
||||
});
|
||||
const settings3 = await getSettings(app);
|
||||
const settings3 = await getUserSettings(app);
|
||||
t.deepEqual(settings3, {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: true,
|
||||
@@ -61,7 +61,7 @@ test('should update user settings', async t => {
|
||||
test('should throw error when update user settings with invalid input', async t => {
|
||||
await app.signup();
|
||||
await t.throwsAsync(
|
||||
updateSettings(app, {
|
||||
updateUserSettings(app, {
|
||||
receiveInvitationEmail: false,
|
||||
// @ts-expect-error invalid value
|
||||
receiveMentionEmail: null,
|
||||
@@ -75,7 +75,7 @@ test('should throw error when update user settings with invalid input', async t
|
||||
test('should not update user settings when not logged in', async t => {
|
||||
await app.logout();
|
||||
await t.throwsAsync(
|
||||
updateSettings(app, {
|
||||
updateUserSettings(app, {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: false,
|
||||
}),
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
Throttle,
|
||||
UserNotFound,
|
||||
} from '../../base';
|
||||
import { Models, SettingsSchema } from '../../models';
|
||||
import { Models, UserSettingsSchema } from '../../models';
|
||||
import { Public } from '../auth/guard';
|
||||
import { sessionUser } from '../auth/service';
|
||||
import { CurrentUser } from '../auth/session';
|
||||
@@ -32,10 +32,10 @@ import {
|
||||
ManageUserInput,
|
||||
PublicUserType,
|
||||
RemoveAvatar,
|
||||
SettingsType,
|
||||
UpdateSettingsInput,
|
||||
UpdateUserInput,
|
||||
UpdateUserSettingsInput,
|
||||
UserOrLimitedUser,
|
||||
UserSettingsType,
|
||||
UserType,
|
||||
} from './types';
|
||||
|
||||
@@ -168,20 +168,20 @@ export class UserSettingsResolver {
|
||||
})
|
||||
async updateSettings(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Args('input', { type: () => UpdateSettingsInput })
|
||||
input: UpdateSettingsInput
|
||||
@Args('input', { type: () => UpdateUserSettingsInput })
|
||||
input: UpdateUserSettingsInput
|
||||
) {
|
||||
SettingsSchema.parse(input);
|
||||
await this.models.settings.set(user.id, input);
|
||||
UserSettingsSchema.parse(input);
|
||||
await this.models.userSettings.set(user.id, input);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ResolveField(() => SettingsType, {
|
||||
@ResolveField(() => UserSettingsType, {
|
||||
name: 'settings',
|
||||
description: 'Get user settings',
|
||||
})
|
||||
async getSettings(@CurrentUser() me: CurrentUser): Promise<SettingsType> {
|
||||
return await this.models.settings.get(me.id);
|
||||
async getSettings(@CurrentUser() me: CurrentUser): Promise<UserSettingsType> {
|
||||
return await this.models.userSettings.get(me.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import type { User } from '@prisma/client';
|
||||
|
||||
import {
|
||||
PublicUser,
|
||||
Settings,
|
||||
SettingsInput,
|
||||
UserSettings,
|
||||
UserSettingsInput,
|
||||
WorkspaceUser,
|
||||
} from '../../models';
|
||||
import { type CurrentUser } from '../auth/session';
|
||||
@@ -115,7 +115,7 @@ export class RemoveAvatar {
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class SettingsType implements Settings {
|
||||
export class UserSettingsType implements UserSettings {
|
||||
@Field({ description: 'Receive invitation email' })
|
||||
receiveInvitationEmail!: boolean;
|
||||
|
||||
@@ -139,7 +139,7 @@ export class ManageUserInput {
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class UpdateSettingsInput implements SettingsInput {
|
||||
export class UpdateUserSettingsInput implements UserSettingsInput {
|
||||
@Field({ description: 'Receive invitation email', nullable: true })
|
||||
receiveInvitationEmail?: boolean;
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ test.after(async t => {
|
||||
});
|
||||
|
||||
test('should get a user settings with default value', async t => {
|
||||
const settings = await t.context.models.settings.get(user.id);
|
||||
const settings = await t.context.models.userSettings.get(user.id);
|
||||
t.deepEqual(settings, {
|
||||
receiveInvitationEmail: true,
|
||||
receiveMentionEmail: true,
|
||||
@@ -51,28 +51,28 @@ test('should get a user settings with default value', async t => {
|
||||
});
|
||||
|
||||
test('should update a user settings', async t => {
|
||||
const settings = await t.context.models.settings.set(user.id, {
|
||||
const settings = await t.context.models.userSettings.set(user.id, {
|
||||
receiveInvitationEmail: false,
|
||||
});
|
||||
t.deepEqual(settings, {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: true,
|
||||
});
|
||||
const settings2 = await t.context.models.settings.get(user.id);
|
||||
const settings2 = await t.context.models.userSettings.get(user.id);
|
||||
t.deepEqual(settings2, settings);
|
||||
|
||||
// update existing setting
|
||||
const setting3 = await t.context.models.settings.set(user.id, {
|
||||
const setting3 = await t.context.models.userSettings.set(user.id, {
|
||||
receiveInvitationEmail: true,
|
||||
});
|
||||
t.deepEqual(setting3, {
|
||||
receiveInvitationEmail: true,
|
||||
receiveMentionEmail: true,
|
||||
});
|
||||
const setting4 = await t.context.models.settings.get(user.id);
|
||||
const setting4 = await t.context.models.userSettings.get(user.id);
|
||||
t.deepEqual(setting4, setting3);
|
||||
|
||||
const setting5 = await t.context.models.settings.set(user.id, {
|
||||
const setting5 = await t.context.models.userSettings.set(user.id, {
|
||||
receiveMentionEmail: false,
|
||||
receiveInvitationEmail: false,
|
||||
});
|
||||
@@ -80,13 +80,13 @@ test('should update a user settings', async t => {
|
||||
receiveInvitationEmail: false,
|
||||
receiveMentionEmail: false,
|
||||
});
|
||||
const setting6 = await t.context.models.settings.get(user.id);
|
||||
const setting6 = await t.context.models.userSettings.get(user.id);
|
||||
t.deepEqual(setting6, setting5);
|
||||
});
|
||||
|
||||
test('should throw error when update settings with invalid payload', async t => {
|
||||
await t.throwsAsync(
|
||||
t.context.models.settings.set(user.id, {
|
||||
t.context.models.userSettings.set(user.id, {
|
||||
// @ts-expect-error invalid setting input types
|
||||
receiveInvitationEmail: 1,
|
||||
}),
|
||||
@@ -17,10 +17,10 @@ import { HistoryModel } from './history';
|
||||
import { NotificationModel } from './notification';
|
||||
import { MODELS_SYMBOL } from './provider';
|
||||
import { SessionModel } from './session';
|
||||
import { SettingsModel } from './settings';
|
||||
import { UserModel } from './user';
|
||||
import { UserDocModel } from './user-doc';
|
||||
import { UserFeatureModel } from './user-feature';
|
||||
import { UserSettingsModel } from './user-settings';
|
||||
import { VerificationTokenModel } from './verification-token';
|
||||
import { WorkspaceModel } from './workspace';
|
||||
import { WorkspaceFeatureModel } from './workspace-feature';
|
||||
@@ -40,7 +40,7 @@ const MODELS = {
|
||||
docUser: DocUserModel,
|
||||
history: HistoryModel,
|
||||
notification: NotificationModel,
|
||||
settings: SettingsModel,
|
||||
userSettings: UserSettingsModel,
|
||||
copilotSession: CopilotSessionModel,
|
||||
copilotContext: CopilotContextModel,
|
||||
copilotJob: CopilotJobModel,
|
||||
@@ -104,10 +104,10 @@ export * from './feature';
|
||||
export * from './history';
|
||||
export * from './notification';
|
||||
export * from './session';
|
||||
export * from './settings';
|
||||
export * from './user';
|
||||
export * from './user-doc';
|
||||
export * from './user-feature';
|
||||
export * from './user-settings';
|
||||
export * from './verification-token';
|
||||
export * from './workspace';
|
||||
export * from './workspace-feature';
|
||||
|
||||
@@ -4,27 +4,27 @@ import z from 'zod';
|
||||
|
||||
import { BaseModel } from './base';
|
||||
|
||||
export const SettingsSchema = z.object({
|
||||
export const UserSettingsSchema = z.object({
|
||||
receiveInvitationEmail: z.boolean().default(true),
|
||||
receiveMentionEmail: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export type SettingsInput = z.input<typeof SettingsSchema>;
|
||||
export type Settings = z.infer<typeof SettingsSchema>;
|
||||
export type UserSettingsInput = z.input<typeof UserSettingsSchema>;
|
||||
export type UserSettings = z.infer<typeof UserSettingsSchema>;
|
||||
|
||||
/**
|
||||
* Settings Model
|
||||
* UserSettings Model
|
||||
*/
|
||||
@Injectable()
|
||||
export class SettingsModel extends BaseModel {
|
||||
export class UserSettingsModel extends BaseModel {
|
||||
@Transactional()
|
||||
async set(userId: string, setting: SettingsInput) {
|
||||
async set(userId: string, setting: UserSettingsInput) {
|
||||
const existsSetting = await this.get(userId);
|
||||
const payload = SettingsSchema.parse({
|
||||
const payload = UserSettingsSchema.parse({
|
||||
...existsSetting,
|
||||
...setting,
|
||||
});
|
||||
await this.db.settings.upsert({
|
||||
await this.db.userSettings.upsert({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
@@ -36,16 +36,16 @@ export class SettingsModel extends BaseModel {
|
||||
payload,
|
||||
},
|
||||
});
|
||||
this.logger.log(`Settings updated for user ${userId}`);
|
||||
this.logger.log(`UserSettings updated for user ${userId}`);
|
||||
return payload;
|
||||
}
|
||||
|
||||
async get(userId: string): Promise<Settings> {
|
||||
const row = await this.db.settings.findUnique({
|
||||
async get(userId: string): Promise<UserSettings> {
|
||||
const row = await this.db.userSettings.findUnique({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
});
|
||||
return SettingsSchema.parse(row?.payload ?? {});
|
||||
return UserSettingsSchema.parse(row?.payload ?? {});
|
||||
}
|
||||
}
|
||||
@@ -1018,7 +1018,7 @@ type Mutation {
|
||||
updateRuntimeConfigs(updates: JSONObject!): [ServerRuntimeConfigType!]!
|
||||
|
||||
"""Update user settings"""
|
||||
updateSettings(input: UpdateSettingsInput!): Boolean!
|
||||
updateSettings(input: UpdateUserSettingsInput!): Boolean!
|
||||
updateSubscriptionRecurring(idempotencyKey: String @deprecated(reason: "use header `Idempotency-Key`"), plan: SubscriptionPlan = Pro, recurring: SubscriptionRecurring!, workspaceId: String): SubscriptionType!
|
||||
|
||||
"""Update an user"""
|
||||
@@ -1363,14 +1363,6 @@ type ServerServiceConfig {
|
||||
name: String!
|
||||
}
|
||||
|
||||
type SettingsType {
|
||||
"""Receive invitation email"""
|
||||
receiveInvitationEmail: Boolean!
|
||||
|
||||
"""Receive mention email"""
|
||||
receiveMentionEmail: Boolean!
|
||||
}
|
||||
|
||||
type SpaceAccessDeniedDataType {
|
||||
spaceId: String!
|
||||
}
|
||||
@@ -1510,7 +1502,12 @@ input UpdateDocUserRoleInput {
|
||||
workspaceId: String!
|
||||
}
|
||||
|
||||
input UpdateSettingsInput {
|
||||
input UpdateUserInput {
|
||||
"""User name"""
|
||||
name: String
|
||||
}
|
||||
|
||||
input UpdateUserSettingsInput {
|
||||
"""Receive invitation email"""
|
||||
receiveInvitationEmail: Boolean
|
||||
|
||||
@@ -1518,11 +1515,6 @@ input UpdateSettingsInput {
|
||||
receiveMentionEmail: Boolean
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
"""User name"""
|
||||
name: String
|
||||
}
|
||||
|
||||
input UpdateWorkspaceInput {
|
||||
"""Enable AI"""
|
||||
enableAi: Boolean
|
||||
@@ -1572,6 +1564,14 @@ type UserQuotaUsageType {
|
||||
storageQuota: SafeInt! @deprecated(reason: "use `UserQuotaType['usedStorageQuota']` instead")
|
||||
}
|
||||
|
||||
type UserSettingsType {
|
||||
"""Receive invitation email"""
|
||||
receiveInvitationEmail: Boolean!
|
||||
|
||||
"""Receive mention email"""
|
||||
receiveMentionEmail: Boolean!
|
||||
}
|
||||
|
||||
type UserType {
|
||||
"""User avatar url"""
|
||||
avatarUrl: String
|
||||
@@ -1612,7 +1612,7 @@ type UserType {
|
||||
quotaUsage: UserQuotaUsageType!
|
||||
|
||||
"""Get user settings"""
|
||||
settings: SettingsType!
|
||||
settings: UserSettingsType!
|
||||
subscriptions: [SubscriptionType!]!
|
||||
token: tokenType! @deprecated(reason: "use [/api/auth/sign-in?native=true] instead")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user