mirror of
https://github.com/colanode/colanode.git
synced 2025-12-25 07:59:35 +01:00
Generate default workspace for new users
This commit is contained in:
215
server/src/lib/workspaces.ts
Normal file
215
server/src/lib/workspaces.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { database } from '@/data/database';
|
||||
import { CreateNode, SelectAccount } from '@/data/schema';
|
||||
import {
|
||||
WorkspaceRole,
|
||||
WorkspaceStatus,
|
||||
WorkspaceUserStatus,
|
||||
} from '@/types/workspaces';
|
||||
import { generateId, IdType } from '@/lib/id';
|
||||
import * as Y from 'yjs';
|
||||
import { fromUint8Array } from 'js-base64';
|
||||
import { NodeCreatedEvent } from '@/types/events';
|
||||
import { enqueueEvent } from '@/queues/events';
|
||||
|
||||
export const createDefaultWorkspace = async (account: SelectAccount) => {
|
||||
const createdAt = new Date();
|
||||
const workspaceId = generateId(IdType.Workspace);
|
||||
const workspaceName = `${account.name}'s Workspace`;
|
||||
|
||||
const user = buildUserNodeCreate(workspaceId, account);
|
||||
const space = buildSpaceNodeCreate(workspaceId, user.id);
|
||||
const page = buildPageNodeCreate(workspaceId, space.id, user.id);
|
||||
const channel = buildChannelNodeCreate(workspaceId, space.id, user.id);
|
||||
|
||||
const nodesToCreate = [user, space, page, channel];
|
||||
|
||||
await database.transaction().execute(async (trx) => {
|
||||
await trx
|
||||
.insertInto('workspaces')
|
||||
.values({
|
||||
id: workspaceId,
|
||||
name: workspaceName,
|
||||
description: 'Personal workspace for ' + account.name,
|
||||
avatar: account.avatar,
|
||||
created_at: createdAt,
|
||||
created_by: account.id,
|
||||
status: WorkspaceStatus.Active,
|
||||
version_id: generateId(IdType.Version),
|
||||
})
|
||||
.execute();
|
||||
|
||||
await trx
|
||||
.insertInto('workspace_users')
|
||||
.values({
|
||||
id: user.id,
|
||||
account_id: account.id,
|
||||
workspace_id: workspaceId,
|
||||
role: WorkspaceRole.Owner,
|
||||
created_at: createdAt,
|
||||
created_by: account.id,
|
||||
status: WorkspaceUserStatus.Active,
|
||||
version_id: generateId(IdType.Version),
|
||||
})
|
||||
.execute();
|
||||
|
||||
await trx.insertInto('nodes').values(nodesToCreate).execute();
|
||||
});
|
||||
|
||||
for (const node of nodesToCreate) {
|
||||
const event = buildNodeCreateEvent(node);
|
||||
await enqueueEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
const buildUserNodeCreate = (
|
||||
workspaceId: string,
|
||||
account: SelectAccount,
|
||||
): CreateNode => {
|
||||
const id = generateId(IdType.User);
|
||||
const versionId = generateId(IdType.Version);
|
||||
const doc = new Y.Doc({
|
||||
guid: id,
|
||||
});
|
||||
|
||||
const attributesMap = doc.getMap('attributes');
|
||||
doc.transact(() => {
|
||||
attributesMap.set('type', 'user');
|
||||
attributesMap.set('name', account.name);
|
||||
attributesMap.set('avatar', account.avatar);
|
||||
attributesMap.set('email', account.email);
|
||||
attributesMap.set('role', WorkspaceRole.Owner);
|
||||
attributesMap.set('accountId', account.id);
|
||||
});
|
||||
|
||||
const attributes = JSON.stringify(attributesMap.toJSON());
|
||||
const state = fromUint8Array(Y.encodeStateAsUpdate(doc));
|
||||
|
||||
return {
|
||||
id,
|
||||
workspace_id: workspaceId,
|
||||
created_at: new Date(),
|
||||
created_by: account.id,
|
||||
version_id: versionId,
|
||||
server_created_at: new Date(),
|
||||
attributes,
|
||||
state,
|
||||
};
|
||||
};
|
||||
|
||||
const buildSpaceNodeCreate = (
|
||||
workspaceId: string,
|
||||
userId: string,
|
||||
): CreateNode => {
|
||||
const id = generateId(IdType.Space);
|
||||
const versionId = generateId(IdType.Version);
|
||||
const doc = new Y.Doc({
|
||||
guid: id,
|
||||
});
|
||||
|
||||
const attributesMap = doc.getMap('attributes');
|
||||
doc.transact(() => {
|
||||
attributesMap.set('type', 'space');
|
||||
attributesMap.set('name', 'Home');
|
||||
attributesMap.set('description', 'Home space');
|
||||
|
||||
attributesMap.set('collaborators', new Y.Map());
|
||||
const collaboratorsMap = attributesMap.get(
|
||||
'collaborators',
|
||||
) as Y.Map<string>;
|
||||
|
||||
collaboratorsMap.set(userId, WorkspaceRole.Owner);
|
||||
});
|
||||
|
||||
const attributes = JSON.stringify(attributesMap.toJSON());
|
||||
const state = fromUint8Array(Y.encodeStateAsUpdate(doc));
|
||||
|
||||
return {
|
||||
id,
|
||||
workspace_id: workspaceId,
|
||||
created_at: new Date(),
|
||||
created_by: userId,
|
||||
version_id: versionId,
|
||||
server_created_at: new Date(),
|
||||
attributes,
|
||||
state,
|
||||
};
|
||||
};
|
||||
|
||||
const buildPageNodeCreate = (
|
||||
workspaceId: string,
|
||||
spaceId: string,
|
||||
userId: string,
|
||||
): CreateNode => {
|
||||
const id = generateId(IdType.Page);
|
||||
const versionId = generateId(IdType.Version);
|
||||
const doc = new Y.Doc({
|
||||
guid: id,
|
||||
});
|
||||
|
||||
const attributesMap = doc.getMap('attributes');
|
||||
doc.transact(() => {
|
||||
attributesMap.set('type', 'page');
|
||||
attributesMap.set('name', 'Notes');
|
||||
attributesMap.set('parentId', spaceId);
|
||||
});
|
||||
|
||||
const attributes = JSON.stringify(attributesMap.toJSON());
|
||||
const state = fromUint8Array(Y.encodeStateAsUpdate(doc));
|
||||
|
||||
return {
|
||||
id,
|
||||
workspace_id: workspaceId,
|
||||
created_at: new Date(),
|
||||
created_by: userId,
|
||||
version_id: versionId,
|
||||
server_created_at: new Date(),
|
||||
attributes,
|
||||
state,
|
||||
};
|
||||
};
|
||||
|
||||
const buildChannelNodeCreate = (
|
||||
workspaceId: string,
|
||||
spaceId: string,
|
||||
userId: string,
|
||||
): CreateNode => {
|
||||
const id = generateId(IdType.Channel);
|
||||
const versionId = generateId(IdType.Version);
|
||||
const doc = new Y.Doc({
|
||||
guid: id,
|
||||
});
|
||||
|
||||
const attributesMap = doc.getMap('attributes');
|
||||
doc.transact(() => {
|
||||
attributesMap.set('type', 'channel');
|
||||
attributesMap.set('parentId', spaceId);
|
||||
attributesMap.set('name', 'Discussions');
|
||||
});
|
||||
|
||||
const attributes = JSON.stringify(attributesMap.toJSON());
|
||||
const state = fromUint8Array(Y.encodeStateAsUpdate(doc));
|
||||
|
||||
return {
|
||||
id,
|
||||
workspace_id: workspaceId,
|
||||
created_at: new Date(),
|
||||
created_by: userId,
|
||||
version_id: versionId,
|
||||
server_created_at: new Date(),
|
||||
attributes,
|
||||
state,
|
||||
};
|
||||
};
|
||||
|
||||
const buildNodeCreateEvent = (node: CreateNode): NodeCreatedEvent => {
|
||||
return {
|
||||
type: 'node_created',
|
||||
id: node.id,
|
||||
workspaceId: node.workspace_id,
|
||||
attributes: JSON.parse(node.attributes ?? '{}'),
|
||||
createdBy: node.created_by,
|
||||
createdAt: node.created_at.toISOString(),
|
||||
versionId: node.version_id,
|
||||
serverCreatedAt: node.server_created_at.toISOString(),
|
||||
};
|
||||
};
|
||||
@@ -26,6 +26,7 @@ import { ServerNodeAttributes } from '@/types/nodes';
|
||||
import { NodeUpdatedEvent } from '@/types/events';
|
||||
import { enqueueEvent } from '@/queues/events';
|
||||
import { SelectAccount } from '@/data/schema';
|
||||
import { createDefaultWorkspace } from '@/lib/workspaces';
|
||||
|
||||
const GoogleUserInfoUrl = 'https://www.googleapis.com/oauth2/v1/userinfo';
|
||||
const SaltRounds = 10;
|
||||
@@ -87,12 +88,7 @@ accountsRouter.post('/register/email', async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
const output = await buildLoginOutput(
|
||||
account.id,
|
||||
account.name,
|
||||
account.email,
|
||||
);
|
||||
|
||||
const output = await buildLoginOutput(account);
|
||||
return res.status(200).json(output);
|
||||
});
|
||||
|
||||
@@ -135,11 +131,7 @@ accountsRouter.post('/login/email', async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
const output = await buildLoginOutput(
|
||||
account.id,
|
||||
account.name,
|
||||
account.email,
|
||||
);
|
||||
const output = await buildLoginOutput(account);
|
||||
return res.status(200).json(output);
|
||||
});
|
||||
|
||||
@@ -187,11 +179,7 @@ accountsRouter.post('/login/google', async (req: Request, res: Response) => {
|
||||
.execute();
|
||||
}
|
||||
|
||||
const output = await buildLoginOutput(
|
||||
existingAccount.id,
|
||||
existingAccount.name,
|
||||
existingAccount.email,
|
||||
);
|
||||
const output = await buildLoginOutput(existingAccount);
|
||||
return res.status(200).json(output);
|
||||
}
|
||||
|
||||
@@ -215,11 +203,7 @@ accountsRouter.post('/login/google', async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
const output = await buildLoginOutput(
|
||||
newAccount.id,
|
||||
newAccount.name,
|
||||
newAccount.email,
|
||||
);
|
||||
const output = await buildLoginOutput(newAccount);
|
||||
return res.status(200).json(output);
|
||||
});
|
||||
|
||||
@@ -400,16 +384,24 @@ accountsRouter.put(
|
||||
);
|
||||
|
||||
const buildLoginOutput = async (
|
||||
id: string,
|
||||
name: string,
|
||||
email: string,
|
||||
account: SelectAccount,
|
||||
): Promise<LoginOutput> => {
|
||||
const workspaceUsers = await database
|
||||
let workspaceUsers = await database
|
||||
.selectFrom('workspace_users')
|
||||
.where('account_id', '=', id)
|
||||
.where('account_id', '=', account.id)
|
||||
.selectAll()
|
||||
.execute();
|
||||
|
||||
if (workspaceUsers.length === 0) {
|
||||
await createDefaultWorkspace(account);
|
||||
|
||||
workspaceUsers = await database
|
||||
.selectFrom('workspace_users')
|
||||
.where('account_id', '=', account.id)
|
||||
.selectAll()
|
||||
.execute();
|
||||
}
|
||||
|
||||
const workspaceOutputs: WorkspaceOutput[] = [];
|
||||
if (workspaceUsers.length > 0) {
|
||||
const workspaceIds = workspaceUsers.map((wu) => wu.workspace_id);
|
||||
@@ -463,7 +455,7 @@ const buildLoginOutput = async (
|
||||
.insertInto('devices')
|
||||
.values({
|
||||
id: deviceId,
|
||||
account_id: id,
|
||||
account_id: account.id,
|
||||
token_hash: hash,
|
||||
token_salt: salt,
|
||||
token_generated_at: new Date(),
|
||||
@@ -481,9 +473,9 @@ const buildLoginOutput = async (
|
||||
return {
|
||||
account: {
|
||||
token,
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
id: account.id,
|
||||
name: account.name,
|
||||
email: account.email,
|
||||
deviceId: device.id,
|
||||
},
|
||||
workspaces: workspaceOutputs,
|
||||
|
||||
Reference in New Issue
Block a user