mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 10:06:17 +08:00
feat(core): integrate realtime features (#15003)
This commit is contained in:
@@ -5,6 +5,17 @@ import { BaseModel } from './base';
|
||||
|
||||
const REDACTED_TOKEN = '[REDACTED]';
|
||||
|
||||
declare global {
|
||||
interface Events {
|
||||
'user.access_token.created': {
|
||||
userId: string;
|
||||
};
|
||||
'user.access_token.revoked': {
|
||||
userId: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface CreateAccessTokenInput {
|
||||
userId: string;
|
||||
name: string;
|
||||
|
||||
@@ -10,6 +10,20 @@ import { BaseModel } from './base';
|
||||
import { DocRole } from './common';
|
||||
import { docRoleFromNew } from './permission-write';
|
||||
|
||||
declare global {
|
||||
interface Events {
|
||||
'doc.grants.changed': {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
};
|
||||
'doc.owner.changed': {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
userId: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DocUserModel extends BaseModel {
|
||||
/**
|
||||
@@ -124,6 +138,14 @@ export class DocUserModel extends BaseModel {
|
||||
return grants.map(grant => this.docGrantToCompat(grant));
|
||||
}
|
||||
|
||||
async findDirectGrantDocIdsByUser(userId: string) {
|
||||
return await this.db.docGrant.findMany({
|
||||
where: { principalType: 'user', principalId: userId },
|
||||
select: { workspaceId: true, docId: true },
|
||||
distinct: ['workspaceId', 'docId'],
|
||||
});
|
||||
}
|
||||
|
||||
count(workspaceId: string, docId: string) {
|
||||
return this.db.docGrant.count({
|
||||
where: {
|
||||
|
||||
@@ -19,6 +19,14 @@ declare global {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
};
|
||||
'doc.public_state.changed': {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
};
|
||||
'doc.default_role.changed': {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,14 @@ import z from 'zod';
|
||||
|
||||
import { BaseModel } from './base';
|
||||
|
||||
declare global {
|
||||
interface Events {
|
||||
'user.settings.updated': {
|
||||
userId: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const UserSettingsSchema = z.object({
|
||||
receiveInvitationEmail: z.boolean().default(true),
|
||||
receiveMentionEmail: z.boolean().default(true),
|
||||
|
||||
@@ -221,7 +221,7 @@ export class UserModel extends BaseModel {
|
||||
});
|
||||
|
||||
this.logger.debug(`User [${user.id}] updated`);
|
||||
this.event.emit('user.updated', user);
|
||||
this.event.emitDetached('user.updated', user);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,36 +186,68 @@ export async function searchCompatRows(
|
||||
pagination: { first: number; offset: number; after?: string | Date }
|
||||
) {
|
||||
const after = pagination.after
|
||||
? Prisma.sql`AND wm.created_at >= ${pagination.after}::timestamptz`
|
||||
? Prisma.sql`AND created_at >= ${pagination.after}::timestamptz`
|
||||
: Prisma.empty;
|
||||
const rows = await db.$queryRaw<WorkspaceUserCompatRow[]>`
|
||||
SELECT
|
||||
COALESCE(wm.legacy_permission_id, wm.id) AS id,
|
||||
wm.workspace_id AS "workspaceId",
|
||||
wm.user_id AS "userId",
|
||||
CASE wm.role
|
||||
WHEN 'owner' THEN ${WorkspaceRole.Owner}
|
||||
WHEN 'admin' THEN ${WorkspaceRole.Admin}
|
||||
ELSE ${WorkspaceRole.Collaborator}
|
||||
END AS type,
|
||||
'Accepted'::"WorkspaceMemberStatus" AS status,
|
||||
CASE wm.source
|
||||
WHEN 'link' THEN 'Link'::"WorkspaceMemberSource"
|
||||
ELSE 'Email'::"WorkspaceMemberSource"
|
||||
END AS source,
|
||||
NULL::varchar AS "inviterId",
|
||||
wm.created_at AS "createdAt",
|
||||
wm.updated_at AS "updatedAt",
|
||||
u.name AS "userName",
|
||||
u.email AS "userEmail",
|
||||
u.avatar_url AS "userAvatarUrl"
|
||||
FROM workspace_members wm
|
||||
INNER JOIN users u ON u.id = wm.user_id
|
||||
WHERE wm.workspace_id = ${workspaceId}
|
||||
AND wm.state = 'active'
|
||||
AND (u.email ILIKE ${`%${query}%`} OR u.name ILIKE ${`%${query}%`})
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT
|
||||
COALESCE(wm.legacy_permission_id, wm.id) AS id,
|
||||
wm.workspace_id AS "workspaceId",
|
||||
wm.user_id AS "userId",
|
||||
CASE wm.role
|
||||
WHEN 'owner' THEN ${WorkspaceRole.Owner}
|
||||
WHEN 'admin' THEN ${WorkspaceRole.Admin}
|
||||
ELSE ${WorkspaceRole.Collaborator}
|
||||
END AS type,
|
||||
'Accepted'::"WorkspaceMemberStatus" AS status,
|
||||
CASE wm.source
|
||||
WHEN 'link' THEN 'Link'::"WorkspaceMemberSource"
|
||||
ELSE 'Email'::"WorkspaceMemberSource"
|
||||
END AS source,
|
||||
NULL::varchar AS "inviterId",
|
||||
wm.created_at AS "createdAt",
|
||||
wm.updated_at AS "updatedAt",
|
||||
u.name AS "userName",
|
||||
u.email AS "userEmail",
|
||||
u.avatar_url AS "userAvatarUrl",
|
||||
wm.created_at AS created_at
|
||||
FROM workspace_members wm
|
||||
INNER JOIN users u ON u.id = wm.user_id
|
||||
WHERE wm.workspace_id = ${workspaceId}
|
||||
AND wm.state = 'active'
|
||||
UNION ALL
|
||||
SELECT
|
||||
COALESCE(wi.legacy_permission_id, wi.id) AS id,
|
||||
wi.workspace_id AS "workspaceId",
|
||||
wi.invitee_user_id AS "userId",
|
||||
CASE wi.requested_role
|
||||
WHEN 'admin' THEN ${WorkspaceRole.Admin}
|
||||
ELSE ${WorkspaceRole.Collaborator}
|
||||
END AS type,
|
||||
CASE wi.status
|
||||
WHEN 'waiting_review' THEN 'UnderReview'::"WorkspaceMemberStatus"
|
||||
WHEN 'waiting_seat' THEN 'NeedMoreSeat'::"WorkspaceMemberStatus"
|
||||
ELSE 'Pending'::"WorkspaceMemberStatus"
|
||||
END AS status,
|
||||
CASE wi.kind
|
||||
WHEN 'link' THEN 'Link'::"WorkspaceMemberSource"
|
||||
ELSE 'Email'::"WorkspaceMemberSource"
|
||||
END AS source,
|
||||
wi.inviter_user_id AS "inviterId",
|
||||
wi.created_at AS "createdAt",
|
||||
wi.updated_at AS "updatedAt",
|
||||
u.name AS "userName",
|
||||
u.email AS "userEmail",
|
||||
u.avatar_url AS "userAvatarUrl",
|
||||
wi.created_at AS created_at
|
||||
FROM workspace_invitations wi
|
||||
INNER JOIN users u ON u.id = wi.invitee_user_id
|
||||
WHERE wi.workspace_id = ${workspaceId}
|
||||
) roles
|
||||
WHERE ("userEmail" ILIKE ${`%${query}%`} OR "userName" ILIKE ${`%${query}%`})
|
||||
${after}
|
||||
ORDER BY wm.created_at ASC
|
||||
ORDER BY created_at ASC
|
||||
OFFSET ${pagination.offset}
|
||||
LIMIT ${pagination.first}
|
||||
`;
|
||||
|
||||
@@ -422,6 +422,26 @@ export class WorkspaceUserModel extends BaseModel {
|
||||
return await findUserActiveWorkspaceRoles(this.db, userId, filter);
|
||||
}
|
||||
|
||||
async getUserWorkspaceIds(userId: string) {
|
||||
const [roles, invitations] = await Promise.all([
|
||||
this.db.workspaceMember.findMany({
|
||||
where: { userId, state: 'active' },
|
||||
select: { workspaceId: true },
|
||||
}),
|
||||
this.db.workspaceInvitation.findMany({
|
||||
where: { inviteeUserId: userId },
|
||||
select: { workspaceId: true },
|
||||
}),
|
||||
]);
|
||||
|
||||
return Array.from(
|
||||
new Set([
|
||||
...roles.map(role => role.workspaceId),
|
||||
...invitations.map(invitation => invitation.workspaceId),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
async hasSharedWorkspace(userId: string, otherUserId: string) {
|
||||
return await hasSharedWorkspace(this.db, userId, otherUserId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user