mirror of
https://github.com/colanode/colanode.git
synced 2025-12-28 16:06:37 +01:00
Restructure and refactor services in desktop
This commit is contained in:
@@ -5,21 +5,20 @@ import { createDebugger } from '@colanode/core';
|
|||||||
import { app, BrowserWindow, ipcMain, protocol, shell } from 'electron';
|
import { app, BrowserWindow, ipcMain, protocol, shell } from 'electron';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { metadataService } from '@/main/services/metadata-service';
|
|
||||||
import { notificationService } from '@/main/services/notification-service';
|
|
||||||
import { WindowSize } from '@/shared/types/metadata';
|
import { WindowSize } from '@/shared/types/metadata';
|
||||||
import { scheduler } from '@/main/scheduler';
|
import { mediator } from '@/main/mediator';
|
||||||
import { assetService } from '@/main/services/asset-service';
|
|
||||||
import { avatarService } from '@/main/services/avatar-service';
|
|
||||||
import { commandService } from '@/main/services/command-service';
|
|
||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { mutationService } from '@/main/services/mutation-service';
|
|
||||||
import { queryService } from '@/main/services/query-service';
|
|
||||||
import { getAppIconPath } from '@/main/utils';
|
import { getAppIconPath } from '@/main/utils';
|
||||||
import { CommandInput, CommandMap } from '@/shared/commands';
|
import { CommandInput, CommandMap } from '@/shared/commands';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { MutationInput, MutationMap } from '@/shared/mutations';
|
import { MutationInput, MutationMap } from '@/shared/mutations';
|
||||||
import { QueryInput, QueryMap } from '@/shared/queries';
|
import { QueryInput, QueryMap } from '@/shared/queries';
|
||||||
|
import { appService } from '@/main/services/app-service';
|
||||||
|
import {
|
||||||
|
handleAssetRequest,
|
||||||
|
handleAvatarRequest,
|
||||||
|
handleFilePreviewRequest,
|
||||||
|
handleFileRequest,
|
||||||
|
} from '@/main/lib/protocols';
|
||||||
|
|
||||||
const debug = createDebugger('desktop:main');
|
const debug = createDebugger('desktop:main');
|
||||||
|
|
||||||
@@ -42,11 +41,10 @@ updateElectronApp({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const createWindow = async () => {
|
const createWindow = async () => {
|
||||||
await scheduler.init();
|
await appService.migrate();
|
||||||
notificationService.checkBadge();
|
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
let windowSize = await metadataService.get<WindowSize>('window_size');
|
let windowSize = await appService.metadata.get<WindowSize>('window_size');
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
width: windowSize?.width ?? 1200,
|
width: windowSize?.width ?? 1200,
|
||||||
height: windowSize?.height ?? 800,
|
height: windowSize?.height ?? 800,
|
||||||
@@ -70,7 +68,7 @@ const createWindow = async () => {
|
|||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
metadataService.set('window_size', windowSize);
|
appService.metadata.set('window_size', windowSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.on('enter-full-screen', () => {
|
mainWindow.on('enter-full-screen', () => {
|
||||||
@@ -80,7 +78,7 @@ const createWindow = async () => {
|
|||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
metadataService.set('window_size', windowSize);
|
appService.metadata.set('window_size', windowSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.on('leave-full-screen', () => {
|
mainWindow.on('leave-full-screen', () => {
|
||||||
@@ -90,7 +88,7 @@ const createWindow = async () => {
|
|||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
metadataService.set('window_size', windowSize);
|
appService.metadata.set('window_size', windowSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
// and load the index.html of the app.
|
// and load the index.html of the app.
|
||||||
@@ -116,25 +114,25 @@ const createWindow = async () => {
|
|||||||
|
|
||||||
if (!protocol.isProtocolHandled('avatar')) {
|
if (!protocol.isProtocolHandled('avatar')) {
|
||||||
protocol.handle('avatar', (request) => {
|
protocol.handle('avatar', (request) => {
|
||||||
return avatarService.handleAvatarRequest(request);
|
return handleAvatarRequest(request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!protocol.isProtocolHandled('local-file')) {
|
if (!protocol.isProtocolHandled('local-file')) {
|
||||||
protocol.handle('local-file', (request) => {
|
protocol.handle('local-file', (request) => {
|
||||||
return fileService.handleFileRequest(request);
|
return handleFileRequest(request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!protocol.isProtocolHandled('local-file-preview')) {
|
if (!protocol.isProtocolHandled('local-file-preview')) {
|
||||||
protocol.handle('local-file-preview', (request) => {
|
protocol.handle('local-file-preview', (request) => {
|
||||||
return fileService.handleFilePreviewRequest(request);
|
return handleFilePreviewRequest(request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!protocol.isProtocolHandled('asset')) {
|
if (!protocol.isProtocolHandled('asset')) {
|
||||||
protocol.handle('asset', (request) => {
|
protocol.handle('asset', (request) => {
|
||||||
return assetService.handleAssetRequest(request);
|
return handleAssetRequest(request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +157,7 @@ app.on('window-all-closed', () => {
|
|||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
queryService.clearSubscriptions();
|
mediator.clearSubscriptions();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
@@ -173,7 +171,7 @@ app.on('activate', () => {
|
|||||||
// In this file you can include the rest of your app's specific main process
|
// In this file you can include the rest of your app's specific main process
|
||||||
// code. You can also put them in separate files and import them here.
|
// code. You can also put them in separate files and import them here.
|
||||||
ipcMain.handle('init', async () => {
|
ipcMain.handle('init', async () => {
|
||||||
await scheduler.init();
|
await appService.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
@@ -182,7 +180,7 @@ ipcMain.handle(
|
|||||||
_: unknown,
|
_: unknown,
|
||||||
input: T
|
input: T
|
||||||
): Promise<MutationMap[T['type']]['output']> => {
|
): Promise<MutationMap[T['type']]['output']> => {
|
||||||
return mutationService.executeMutation(input);
|
return mediator.executeMutation(input);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -192,7 +190,7 @@ ipcMain.handle(
|
|||||||
_: unknown,
|
_: unknown,
|
||||||
input: T
|
input: T
|
||||||
): Promise<QueryMap[T['type']]['output']> => {
|
): Promise<QueryMap[T['type']]['output']> => {
|
||||||
return queryService.executeQuery(input);
|
return mediator.executeQuery(input);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -203,12 +201,12 @@ ipcMain.handle(
|
|||||||
id: string,
|
id: string,
|
||||||
input: T
|
input: T
|
||||||
): Promise<QueryMap[T['type']]['output']> => {
|
): Promise<QueryMap[T['type']]['output']> => {
|
||||||
return queryService.executeQueryAndSubscribe(id, input);
|
return mediator.executeQueryAndSubscribe(id, input);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle('unsubscribe-query', (_: unknown, id: string): void => {
|
ipcMain.handle('unsubscribe-query', (_: unknown, id: string): void => {
|
||||||
queryService.unsubscribeQuery(id);
|
mediator.unsubscribeQuery(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
@@ -217,6 +215,6 @@ ipcMain.handle(
|
|||||||
_: unknown,
|
_: unknown,
|
||||||
input: T
|
input: T
|
||||||
): Promise<CommandMap[T['type']]['output']> => {
|
): Promise<CommandMap[T['type']]['output']> => {
|
||||||
return commandService.executeCommand(input);
|
return mediator.executeCommand(input);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { fileService } from '@/main/services/file-service';
|
import path from 'path';
|
||||||
|
import { shell } from 'electron';
|
||||||
|
|
||||||
|
import { getWorkspaceFilesDirectoryPath } from '@/main/utils';
|
||||||
import { CommandHandler } from '@/main/types';
|
import { CommandHandler } from '@/main/types';
|
||||||
import { FileOpenCommandInput } from '@/shared/commands/file-open';
|
import { FileOpenCommandInput } from '@/shared/commands/file-open';
|
||||||
|
|
||||||
@@ -6,6 +9,16 @@ export class FileOpenCommandHandler
|
|||||||
implements CommandHandler<FileOpenCommandInput>
|
implements CommandHandler<FileOpenCommandInput>
|
||||||
{
|
{
|
||||||
public async handleCommand(input: FileOpenCommandInput): Promise<void> {
|
public async handleCommand(input: FileOpenCommandInput): Promise<void> {
|
||||||
fileService.openFile(input.userId, input.fileId, input.extension);
|
const workspaceFilesDir = getWorkspaceFilesDirectoryPath(
|
||||||
|
input.accountId,
|
||||||
|
input.workspaceId
|
||||||
|
);
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
workspaceFilesDir,
|
||||||
|
`${input.fileId}${input.extension}`
|
||||||
|
);
|
||||||
|
|
||||||
|
shell.openPath(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
import { Migration } from 'kysely';
|
|
||||||
|
|
||||||
const createServersTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('servers')
|
|
||||||
.addColumn('domain', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('name', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('avatar', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('attributes', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('version', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('last_synced_at', 'text')
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db
|
|
||||||
.insertInto('servers')
|
|
||||||
.values([
|
|
||||||
{
|
|
||||||
domain: 'eu.colanode.com',
|
|
||||||
name: 'Colanode Cloud (EU)',
|
|
||||||
avatar: 'https://colanode.com/assets/flags/eu.svg',
|
|
||||||
attributes: '{}',
|
|
||||||
version: '0.1.0',
|
|
||||||
created_at: new Date().toISOString(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
domain: 'us.colanode.com',
|
|
||||||
name: 'Colanode Cloud (US)',
|
|
||||||
avatar: 'https://colanode.com/assets/flags/us.svg',
|
|
||||||
attributes: '{}',
|
|
||||||
version: '0.1.0',
|
|
||||||
created_at: new Date().toISOString(),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('servers').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createAccountsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('accounts')
|
|
||||||
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('device_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('server', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('name', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('email', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('avatar', 'text')
|
|
||||||
.addColumn('token', 'text', (col) => col.notNull())
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('accounts').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createWorkspacesTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('workspaces')
|
|
||||||
.addColumn('user_id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('workspace_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('account_id', 'text', (col) =>
|
|
||||||
col.notNull().references('accounts.id').onDelete('cascade')
|
|
||||||
)
|
|
||||||
.addColumn('name', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('description', 'text')
|
|
||||||
.addColumn('avatar', 'text')
|
|
||||||
.addColumn('role', 'text', (col) => col.notNull())
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('workspaces').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createDeletedTokensTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('deleted_tokens')
|
|
||||||
.addColumn('account_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('token', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('server', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('deleted_tokens').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMetadataTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('metadata')
|
|
||||||
.addColumn('key', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('value', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('metadata').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const appDatabaseMigrations: Record<string, Migration> = {
|
|
||||||
'00001_create_servers_table': createServersTable,
|
|
||||||
'00002_create_accounts_table': createAccountsTable,
|
|
||||||
'00003_create_workspaces_table': createWorkspacesTable,
|
|
||||||
'00004_create_deleted_tokens_table': createDeletedTokensTable,
|
|
||||||
'00005_create_metadata_table': createMetadataTable,
|
|
||||||
};
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
import SQLite from 'better-sqlite3';
|
|
||||||
import { Kysely, Migration, Migrator, SqliteDialect } from 'kysely';
|
|
||||||
import { createDebugger } from '@colanode/core';
|
|
||||||
|
|
||||||
import fs from 'fs';
|
|
||||||
|
|
||||||
import { appDatabaseMigrations } from '@/main/data/app/migrations';
|
|
||||||
import { AppDatabaseSchema } from '@/main/data/app/schema';
|
|
||||||
import { workspaceDatabaseMigrations } from '@/main/data/workspace/migrations';
|
|
||||||
import { WorkspaceDatabaseSchema } from '@/main/data/workspace/schema';
|
|
||||||
import { appDatabasePath, getWorkspaceDirectoryPath } from '@/main/utils';
|
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
|
||||||
|
|
||||||
class DatabaseService {
|
|
||||||
private initPromise: Promise<void> | null = null;
|
|
||||||
private readonly workspaceDatabases: Map<
|
|
||||||
string,
|
|
||||||
Kysely<WorkspaceDatabaseSchema>
|
|
||||||
> = new Map();
|
|
||||||
|
|
||||||
public readonly appDatabase: Kysely<AppDatabaseSchema>;
|
|
||||||
private readonly debug = createDebugger('desktop:service:database');
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.debug('Constructing database service');
|
|
||||||
const dialect = new SqliteDialect({
|
|
||||||
database: this.buildSqlite(appDatabasePath),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.appDatabase = new Kysely<AppDatabaseSchema>({ dialect });
|
|
||||||
|
|
||||||
eventBus.subscribe((event) => {
|
|
||||||
if (event.type === 'workspace_created') {
|
|
||||||
this.initWorkspaceDatabase(event.workspace.userId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
|
||||||
this.debug('Initializing database service');
|
|
||||||
if (!this.initPromise) {
|
|
||||||
this.initPromise = this.executeInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.initPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getWorkspaceDatabase(
|
|
||||||
userId: string
|
|
||||||
): Promise<Kysely<WorkspaceDatabaseSchema>> {
|
|
||||||
await this.waitForInit();
|
|
||||||
|
|
||||||
if (this.workspaceDatabases.has(userId)) {
|
|
||||||
return this.workspaceDatabases.get(userId)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
//try and check if it's in database but hasn't been loaded yet
|
|
||||||
const workspace = await this.appDatabase
|
|
||||||
.selectFrom('workspaces')
|
|
||||||
.selectAll()
|
|
||||||
.where('user_id', '=', userId)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!workspace) {
|
|
||||||
throw new Error('Workspace database not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const workspaceDatabase = await this.initWorkspaceDatabase(userId);
|
|
||||||
this.workspaceDatabases.set(userId, workspaceDatabase);
|
|
||||||
return workspaceDatabase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getWorkspaceDatabases(): Promise<
|
|
||||||
Map<string, Kysely<WorkspaceDatabaseSchema>>
|
|
||||||
> {
|
|
||||||
await this.waitForInit();
|
|
||||||
return this.workspaceDatabases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async removeWorkspaceDatabase(userId: string): Promise<void> {
|
|
||||||
this.debug(`Deleting workspace database for user: ${userId}`);
|
|
||||||
await this.waitForInit();
|
|
||||||
|
|
||||||
const workspaceDatabase = this.workspaceDatabases.get(userId);
|
|
||||||
if (workspaceDatabase) {
|
|
||||||
try {
|
|
||||||
workspaceDatabase.destroy();
|
|
||||||
} catch (error) {
|
|
||||||
this.debug(
|
|
||||||
`Failed to destroy workspace database for user: ${userId}`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.workspaceDatabases.delete(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async waitForInit(): Promise<void> {
|
|
||||||
if (!this.initPromise) {
|
|
||||||
this.initPromise = this.executeInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.initPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async executeInit(): Promise<void> {
|
|
||||||
await this.migrateAppDatabase();
|
|
||||||
|
|
||||||
const workspaces = await this.appDatabase
|
|
||||||
.selectFrom('workspaces')
|
|
||||||
.select('user_id')
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
for (const workspace of workspaces) {
|
|
||||||
const workspaceDatabase = await this.initWorkspaceDatabase(
|
|
||||||
workspace.user_id
|
|
||||||
);
|
|
||||||
|
|
||||||
this.workspaceDatabases.set(workspace.user_id, workspaceDatabase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async initWorkspaceDatabase(
|
|
||||||
userId: string
|
|
||||||
): Promise<Kysely<WorkspaceDatabaseSchema>> {
|
|
||||||
this.debug(`Initializing workspace database for user: ${userId}`);
|
|
||||||
const workspaceDir = getWorkspaceDirectoryPath(userId);
|
|
||||||
|
|
||||||
if (!fs.existsSync(workspaceDir)) {
|
|
||||||
fs.mkdirSync(workspaceDir, {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const dialect = new SqliteDialect({
|
|
||||||
database: this.buildSqlite(`${workspaceDir}/workspace.db`),
|
|
||||||
});
|
|
||||||
|
|
||||||
const workspaceDatabase = new Kysely<WorkspaceDatabaseSchema>({
|
|
||||||
dialect,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.migrateWorkspaceDatabase(workspaceDatabase);
|
|
||||||
return workspaceDatabase;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async migrateAppDatabase(): Promise<void> {
|
|
||||||
this.debug('Migrating app database');
|
|
||||||
const migrator = new Migrator({
|
|
||||||
db: this.appDatabase,
|
|
||||||
provider: {
|
|
||||||
getMigrations(): Promise<Record<string, Migration>> {
|
|
||||||
return Promise.resolve(appDatabaseMigrations);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await migrator.migrateToLatest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async migrateWorkspaceDatabase(
|
|
||||||
database: Kysely<WorkspaceDatabaseSchema>
|
|
||||||
): Promise<void> {
|
|
||||||
this.debug('Migrating workspace database');
|
|
||||||
const migrator = new Migrator({
|
|
||||||
db: database,
|
|
||||||
provider: {
|
|
||||||
getMigrations(): Promise<Record<string, Migration>> {
|
|
||||||
return Promise.resolve(workspaceDatabaseMigrations);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await migrator.migrateToLatest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildSqlite = (filename: string): SQLite.Database => {
|
|
||||||
this.debug(`Building sqlite database: ${filename}`);
|
|
||||||
const database = new SQLite(filename);
|
|
||||||
database.pragma('journal_mode = WAL');
|
|
||||||
return database;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const databaseService = new DatabaseService();
|
|
||||||
@@ -1,401 +0,0 @@
|
|||||||
import { Migration, sql } from 'kysely';
|
|
||||||
|
|
||||||
const createUsersTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('users')
|
|
||||||
.addColumn('id', 'text', (col) => col.primaryKey().notNull())
|
|
||||||
.addColumn('email', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('name', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('avatar', 'text')
|
|
||||||
.addColumn('custom_name', 'text')
|
|
||||||
.addColumn('custom_avatar', 'text')
|
|
||||||
.addColumn('role', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('storage_limit', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('max_file_size', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('status', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.addColumn('version', 'integer')
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('users').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createEntriesTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('entries')
|
|
||||||
.addColumn('id', 'text', (col) => col.primaryKey().notNull())
|
|
||||||
.addColumn('type', 'text', (col) =>
|
|
||||||
col
|
|
||||||
.notNull()
|
|
||||||
.generatedAlwaysAs(sql`json_extract(attributes, '$.type')`)
|
|
||||||
.stored()
|
|
||||||
)
|
|
||||||
.addColumn('parent_id', 'text', (col) =>
|
|
||||||
col
|
|
||||||
.generatedAlwaysAs(sql`json_extract(attributes, '$.parentId')`)
|
|
||||||
.stored()
|
|
||||||
)
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('attributes', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.addColumn('created_by', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_by', 'text')
|
|
||||||
.addColumn('transaction_id', 'text', (col) => col.notNull())
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
|
||||||
.createIndex('entries_parent_id_type_index')
|
|
||||||
.on('entries')
|
|
||||||
.columns(['parent_id', 'type'])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('entries').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createEntryTransactionsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('entry_transactions')
|
|
||||||
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('entry_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('operation', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('data', 'blob')
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_by', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('server_created_at', 'text')
|
|
||||||
.addColumn('version', 'integer', (col) => col.notNull())
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('entry_transactions').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createEntryInteractionsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('entry_interactions')
|
|
||||||
.addColumn('entry_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('first_seen_at', 'text')
|
|
||||||
.addColumn('last_seen_at', 'text')
|
|
||||||
.addColumn('first_opened_at', 'text')
|
|
||||||
.addColumn('last_opened_at', 'text')
|
|
||||||
.addColumn('version', 'integer', (col) => col.notNull())
|
|
||||||
.addPrimaryKeyConstraint('entry_interactions_pkey', [
|
|
||||||
'entry_id',
|
|
||||||
'collaborator_id',
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('entry_interactions').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createCollaborationsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('collaborations')
|
|
||||||
.addColumn('entry_id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('role', 'text')
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.addColumn('deleted_at', 'text')
|
|
||||||
.addColumn('version', 'integer')
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('collaborations').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMessagesTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('messages')
|
|
||||||
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('type', 'integer', (col) =>
|
|
||||||
col
|
|
||||||
.notNull()
|
|
||||||
.generatedAlwaysAs(sql`json_extract(attributes, '$.type')`)
|
|
||||||
.stored()
|
|
||||||
)
|
|
||||||
.addColumn('parent_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('entry_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('attributes', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_by', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.addColumn('updated_by', 'text')
|
|
||||||
.addColumn('deleted_at', 'text')
|
|
||||||
.addColumn('version', 'integer')
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
|
||||||
.createIndex('messages_parent_id_index')
|
|
||||||
.on('messages')
|
|
||||||
.columns(['parent_id'])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('messages').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMessageReactionsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('message_reactions')
|
|
||||||
.addColumn('message_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('reaction', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('deleted_at', 'text')
|
|
||||||
.addColumn('version', 'integer', (col) => col.notNull())
|
|
||||||
.addPrimaryKeyConstraint('message_reactions_pkey', [
|
|
||||||
'message_id',
|
|
||||||
'collaborator_id',
|
|
||||||
'reaction',
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('message_reactions').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMessageInteractionsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('message_interactions')
|
|
||||||
.addColumn('message_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('first_seen_at', 'text')
|
|
||||||
.addColumn('last_seen_at', 'text')
|
|
||||||
.addColumn('first_opened_at', 'text')
|
|
||||||
.addColumn('last_opened_at', 'text')
|
|
||||||
.addColumn('version', 'integer', (col) => col.notNull())
|
|
||||||
.addPrimaryKeyConstraint('message_interactions_pkey', [
|
|
||||||
'message_id',
|
|
||||||
'collaborator_id',
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('message_interactions').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFilesTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('files')
|
|
||||||
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('type', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('parent_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('entry_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('name', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('original_name', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('mime_type', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('extension', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('size', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_by', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.addColumn('updated_by', 'text')
|
|
||||||
.addColumn('deleted_at', 'text')
|
|
||||||
.addColumn('status', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('version', 'integer')
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
|
||||||
.createIndex('files_parent_id_index')
|
|
||||||
.on('files')
|
|
||||||
.columns(['parent_id'])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('files').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFileStatesTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('file_states')
|
|
||||||
.addColumn('file_id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('download_status', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('download_progress', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('download_retries', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('upload_status', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('upload_progress', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('upload_retries', 'integer', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('file_states').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFileInteractionsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('file_interactions')
|
|
||||||
.addColumn('file_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('root_id', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('seen_at', 'text')
|
|
||||||
.addColumn('first_opened_at', 'text')
|
|
||||||
.addColumn('last_opened_at', 'text')
|
|
||||||
.addColumn('version', 'integer', (col) => col.notNull())
|
|
||||||
.addPrimaryKeyConstraint('file_interactions_pkey', [
|
|
||||||
'file_id',
|
|
||||||
'collaborator_id',
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('file_interactions').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMutationsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('mutations')
|
|
||||||
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('type', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('data', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('retries', 'integer', (col) => col.notNull())
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('mutations').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createEntryPathsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('entry_paths')
|
|
||||||
.addColumn('ancestor_id', 'varchar(30)', (col) =>
|
|
||||||
col.notNull().references('entries.id').onDelete('cascade')
|
|
||||||
)
|
|
||||||
.addColumn('descendant_id', 'varchar(30)', (col) =>
|
|
||||||
col.notNull().references('entries.id').onDelete('cascade')
|
|
||||||
)
|
|
||||||
.addColumn('level', 'integer', (col) => col.notNull())
|
|
||||||
.addPrimaryKeyConstraint('entry_paths_pkey', [
|
|
||||||
'ancestor_id',
|
|
||||||
'descendant_id',
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await sql`
|
|
||||||
CREATE TRIGGER trg_insert_entry_path
|
|
||||||
AFTER INSERT ON entries
|
|
||||||
FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
-- Insert direct path from the new entry to itself
|
|
||||||
INSERT INTO entry_paths (ancestor_id, descendant_id, level)
|
|
||||||
VALUES (NEW.id, NEW.id, 0);
|
|
||||||
|
|
||||||
-- Insert paths from ancestors to the new entry
|
|
||||||
INSERT INTO entry_paths (ancestor_id, descendant_id, level)
|
|
||||||
SELECT ancestor_id, NEW.id, level + 1
|
|
||||||
FROM entry_paths
|
|
||||||
WHERE descendant_id = NEW.parent_id AND ancestor_id <> NEW.id;
|
|
||||||
END;
|
|
||||||
`.execute(db);
|
|
||||||
|
|
||||||
await sql`
|
|
||||||
CREATE TRIGGER trg_update_entry_path
|
|
||||||
AFTER UPDATE ON entries
|
|
||||||
FOR EACH ROW
|
|
||||||
WHEN OLD.parent_id <> NEW.parent_id
|
|
||||||
BEGIN
|
|
||||||
-- Delete old paths involving the updated entry
|
|
||||||
DELETE FROM entry_paths
|
|
||||||
WHERE descendant_id = NEW.id AND ancestor_id <> NEW.id;
|
|
||||||
|
|
||||||
-- Insert new paths from ancestors to the updated entry
|
|
||||||
INSERT INTO entry_paths (ancestor_id, descendant_id, level)
|
|
||||||
SELECT ancestor_id, NEW.id, level + 1
|
|
||||||
FROM entry_paths
|
|
||||||
WHERE descendant_id = NEW.parent_id AND ancestor_id <> NEW.id;
|
|
||||||
END;
|
|
||||||
`.execute(db);
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await sql`
|
|
||||||
DROP TRIGGER IF EXISTS trg_insert_entry_path;
|
|
||||||
DROP TRIGGER IF EXISTS trg_update_entry_path;
|
|
||||||
`.execute(db);
|
|
||||||
|
|
||||||
await db.schema.dropTable('entry_paths').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createTextsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await sql`
|
|
||||||
CREATE VIRTUAL TABLE texts USING fts5(id UNINDEXED, name, text);
|
|
||||||
`.execute(db);
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await sql`
|
|
||||||
DROP TABLE IF EXISTS texts;
|
|
||||||
`.execute(db);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createCursorsTable: Migration = {
|
|
||||||
up: async (db) => {
|
|
||||||
await db.schema
|
|
||||||
.createTable('cursors')
|
|
||||||
.addColumn('key', 'text', (col) => col.notNull().primaryKey())
|
|
||||||
.addColumn('value', 'integer', (col) => col.notNull().defaultTo(0))
|
|
||||||
.addColumn('created_at', 'text', (col) => col.notNull())
|
|
||||||
.addColumn('updated_at', 'text')
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
down: async (db) => {
|
|
||||||
await db.schema.dropTable('cursors').execute();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const workspaceDatabaseMigrations: Record<string, Migration> = {
|
|
||||||
'00001_create_users_table': createUsersTable,
|
|
||||||
'00002_create_entries_table': createEntriesTable,
|
|
||||||
'00003_create_entry_interactions_table': createEntryInteractionsTable,
|
|
||||||
'00004_create_entry_transactions_table': createEntryTransactionsTable,
|
|
||||||
'00005_create_collaborations_table': createCollaborationsTable,
|
|
||||||
'00006_create_messages_table': createMessagesTable,
|
|
||||||
'00007_create_message_reactions_table': createMessageReactionsTable,
|
|
||||||
'00008_create_message_interactions_table': createMessageInteractionsTable,
|
|
||||||
'00009_create_files_table': createFilesTable,
|
|
||||||
'00010_create_file_states_table': createFileStatesTable,
|
|
||||||
'00011_create_file_interactions_table': createFileInteractionsTable,
|
|
||||||
'00012_create_mutations_table': createMutationsTable,
|
|
||||||
'00013_create_entry_paths_table': createEntryPathsTable,
|
|
||||||
'00014_create_texts_table': createTextsTable,
|
|
||||||
'00015_create_cursors_table': createCursorsTable,
|
|
||||||
};
|
|
||||||
2
apps/desktop/src/main/databases/account/index.ts
Normal file
2
apps/desktop/src/main/databases/account/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './schema';
|
||||||
|
export * from './migrations';
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createWorkspacesTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('workspaces')
|
||||||
|
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('user_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('account_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('name', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('description', 'text')
|
||||||
|
.addColumn('avatar', 'text')
|
||||||
|
.addColumn('role', 'text', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('workspaces').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
import { createWorkspacesTable } from './00001-create-workspaces-table';
|
||||||
|
|
||||||
|
export const accountDatabaseMigrations: Record<string, Migration> = {
|
||||||
|
'00001-create-workspaces-table': createWorkspacesTable,
|
||||||
|
};
|
||||||
20
apps/desktop/src/main/databases/account/schema.ts
Normal file
20
apps/desktop/src/main/databases/account/schema.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { WorkspaceRole } from '@colanode/core';
|
||||||
|
import { ColumnType, Insertable, Selectable, Updateable } from 'kysely';
|
||||||
|
|
||||||
|
interface WorkspaceTable {
|
||||||
|
id: ColumnType<string, string, never>;
|
||||||
|
user_id: ColumnType<string, string, never>;
|
||||||
|
account_id: ColumnType<string, string, never>;
|
||||||
|
name: ColumnType<string, string, string>;
|
||||||
|
description: ColumnType<string | null, string | null, string | null>;
|
||||||
|
avatar: ColumnType<string | null, string | null, string | null>;
|
||||||
|
role: ColumnType<WorkspaceRole, WorkspaceRole, WorkspaceRole>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SelectWorkspace = Selectable<WorkspaceTable>;
|
||||||
|
export type CreateWorkspace = Insertable<WorkspaceTable>;
|
||||||
|
export type UpdateWorkspace = Updateable<WorkspaceTable>;
|
||||||
|
|
||||||
|
export interface AccountDatabaseSchema {
|
||||||
|
workspaces: WorkspaceTable;
|
||||||
|
}
|
||||||
2
apps/desktop/src/main/databases/app/index.ts
Normal file
2
apps/desktop/src/main/databases/app/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './schema';
|
||||||
|
export * from './migrations';
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createServersTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('servers')
|
||||||
|
.addColumn('domain', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('name', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('avatar', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('attributes', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('version', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('last_synced_at', 'text')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db
|
||||||
|
.insertInto('servers')
|
||||||
|
.values([
|
||||||
|
{
|
||||||
|
domain: 'eu.colanode.com',
|
||||||
|
name: 'Colanode Cloud (EU)',
|
||||||
|
avatar: 'https://colanode.com/assets/flags/eu.svg',
|
||||||
|
attributes: '{}',
|
||||||
|
version: '0.1.0',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: 'us.colanode.com',
|
||||||
|
name: 'Colanode Cloud (US)',
|
||||||
|
avatar: 'https://colanode.com/assets/flags/us.svg',
|
||||||
|
attributes: '{}',
|
||||||
|
version: '0.1.0',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('servers').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createAccountsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('accounts')
|
||||||
|
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('device_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('server', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('name', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('email', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('avatar', 'text')
|
||||||
|
.addColumn('token', 'text', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('accounts').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createDeletedTokensTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('deleted_tokens')
|
||||||
|
.addColumn('account_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('token', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('server', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('deleted_tokens').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createMetadataTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('metadata')
|
||||||
|
.addColumn('key', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('value', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('metadata').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
13
apps/desktop/src/main/databases/app/migrations/index.ts
Normal file
13
apps/desktop/src/main/databases/app/migrations/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
import { createServersTable } from './00001-create-servers-table';
|
||||||
|
import { createAccountsTable } from './00002-create-accounts-table';
|
||||||
|
import { createDeletedTokensTable } from './00003-create-deleted-tokens-table';
|
||||||
|
import { createMetadataTable } from './00004-create-metadata-table';
|
||||||
|
|
||||||
|
export const appDatabaseMigrations: Record<string, Migration> = {
|
||||||
|
'00001-create-servers-table': createServersTable,
|
||||||
|
'00002-create-accounts-table': createAccountsTable,
|
||||||
|
'00003-create-deleted-tokens-table': createDeletedTokensTable,
|
||||||
|
'00004-create-metadata-table': createMetadataTable,
|
||||||
|
};
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { WorkspaceRole } from '@colanode/core';
|
|
||||||
import { ColumnType, Insertable, Selectable, Updateable } from 'kysely';
|
import { ColumnType, Insertable, Selectable, Updateable } from 'kysely';
|
||||||
|
|
||||||
interface ServerTable {
|
interface ServerTable {
|
||||||
@@ -29,20 +28,6 @@ export type SelectAccount = Selectable<AccountTable>;
|
|||||||
export type CreateAccount = Insertable<AccountTable>;
|
export type CreateAccount = Insertable<AccountTable>;
|
||||||
export type UpdateAccount = Updateable<AccountTable>;
|
export type UpdateAccount = Updateable<AccountTable>;
|
||||||
|
|
||||||
interface WorkspaceTable {
|
|
||||||
user_id: ColumnType<string, string, never>;
|
|
||||||
workspace_id: ColumnType<string, string, never>;
|
|
||||||
account_id: ColumnType<string, string, never>;
|
|
||||||
name: ColumnType<string, string, string>;
|
|
||||||
description: ColumnType<string | null, string | null, string | null>;
|
|
||||||
avatar: ColumnType<string | null, string | null, string | null>;
|
|
||||||
role: ColumnType<WorkspaceRole, WorkspaceRole, WorkspaceRole>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SelectWorkspace = Selectable<WorkspaceTable>;
|
|
||||||
export type CreateWorkspace = Insertable<WorkspaceTable>;
|
|
||||||
export type UpdateWorkspace = Updateable<WorkspaceTable>;
|
|
||||||
|
|
||||||
interface DeletedTokenTable {
|
interface DeletedTokenTable {
|
||||||
token: ColumnType<string, string, never>;
|
token: ColumnType<string, string, never>;
|
||||||
account_id: ColumnType<string, string, never>;
|
account_id: ColumnType<string, string, never>;
|
||||||
@@ -64,7 +49,6 @@ export type UpdateMetadata = Updateable<MetadataTable>;
|
|||||||
export interface AppDatabaseSchema {
|
export interface AppDatabaseSchema {
|
||||||
servers: ServerTable;
|
servers: ServerTable;
|
||||||
accounts: AccountTable;
|
accounts: AccountTable;
|
||||||
workspaces: WorkspaceTable;
|
|
||||||
deleted_tokens: DeletedTokenTable;
|
deleted_tokens: DeletedTokenTable;
|
||||||
metadata: MetadataTable;
|
metadata: MetadataTable;
|
||||||
}
|
}
|
||||||
2
apps/desktop/src/main/databases/workspace/index.ts
Normal file
2
apps/desktop/src/main/databases/workspace/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './schema';
|
||||||
|
export * from './migrations';
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createUsersTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('users')
|
||||||
|
.addColumn('id', 'text', (col) => col.primaryKey().notNull())
|
||||||
|
.addColumn('email', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('name', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('avatar', 'text')
|
||||||
|
.addColumn('custom_name', 'text')
|
||||||
|
.addColumn('custom_avatar', 'text')
|
||||||
|
.addColumn('role', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('storage_limit', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('max_file_size', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('status', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.addColumn('version', 'integer')
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('users').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Migration, sql } from 'kysely';
|
||||||
|
|
||||||
|
export const createEntriesTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('entries')
|
||||||
|
.addColumn('id', 'text', (col) => col.primaryKey().notNull())
|
||||||
|
.addColumn('type', 'text', (col) =>
|
||||||
|
col
|
||||||
|
.notNull()
|
||||||
|
.generatedAlwaysAs(sql`json_extract(attributes, '$.type')`)
|
||||||
|
.stored()
|
||||||
|
)
|
||||||
|
.addColumn('parent_id', 'text', (col) =>
|
||||||
|
col
|
||||||
|
.generatedAlwaysAs(sql`json_extract(attributes, '$.parentId')`)
|
||||||
|
.stored()
|
||||||
|
)
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('attributes', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.addColumn('created_by', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_by', 'text')
|
||||||
|
.addColumn('transaction_id', 'text', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('entries_parent_id_type_index')
|
||||||
|
.on('entries')
|
||||||
|
.columns(['parent_id', 'type'])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('entries').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createEntryTransactionsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('entry_transactions')
|
||||||
|
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('entry_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('operation', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('data', 'blob')
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_by', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('server_created_at', 'text')
|
||||||
|
.addColumn('version', 'integer', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('entry_transactions').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createEntryInteractionsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('entry_interactions')
|
||||||
|
.addColumn('entry_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('first_seen_at', 'text')
|
||||||
|
.addColumn('last_seen_at', 'text')
|
||||||
|
.addColumn('first_opened_at', 'text')
|
||||||
|
.addColumn('last_opened_at', 'text')
|
||||||
|
.addColumn('version', 'integer', (col) => col.notNull())
|
||||||
|
.addPrimaryKeyConstraint('entry_interactions_pkey', [
|
||||||
|
'entry_id',
|
||||||
|
'collaborator_id',
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('entry_interactions').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createCollaborationsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('collaborations')
|
||||||
|
.addColumn('entry_id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('role', 'text')
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.addColumn('deleted_at', 'text')
|
||||||
|
.addColumn('version', 'integer')
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('collaborations').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Migration, sql } from 'kysely';
|
||||||
|
|
||||||
|
export const createMessagesTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('messages')
|
||||||
|
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('type', 'integer', (col) =>
|
||||||
|
col
|
||||||
|
.notNull()
|
||||||
|
.generatedAlwaysAs(sql`json_extract(attributes, '$.type')`)
|
||||||
|
.stored()
|
||||||
|
)
|
||||||
|
.addColumn('parent_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('entry_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('attributes', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_by', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.addColumn('updated_by', 'text')
|
||||||
|
.addColumn('deleted_at', 'text')
|
||||||
|
.addColumn('version', 'integer')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('messages_parent_id_index')
|
||||||
|
.on('messages')
|
||||||
|
.columns(['parent_id'])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('messages').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createMessageReactionsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('message_reactions')
|
||||||
|
.addColumn('message_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('reaction', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('deleted_at', 'text')
|
||||||
|
.addColumn('version', 'integer', (col) => col.notNull())
|
||||||
|
.addPrimaryKeyConstraint('message_reactions_pkey', [
|
||||||
|
'message_id',
|
||||||
|
'collaborator_id',
|
||||||
|
'reaction',
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('message_reactions').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createMessageInteractionsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('message_interactions')
|
||||||
|
.addColumn('message_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('first_seen_at', 'text')
|
||||||
|
.addColumn('last_seen_at', 'text')
|
||||||
|
.addColumn('first_opened_at', 'text')
|
||||||
|
.addColumn('last_opened_at', 'text')
|
||||||
|
.addColumn('version', 'integer', (col) => col.notNull())
|
||||||
|
.addPrimaryKeyConstraint('message_interactions_pkey', [
|
||||||
|
'message_id',
|
||||||
|
'collaborator_id',
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('message_interactions').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createFilesTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('files')
|
||||||
|
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('type', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('parent_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('entry_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('name', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('original_name', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('mime_type', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('extension', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('size', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_by', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.addColumn('updated_by', 'text')
|
||||||
|
.addColumn('deleted_at', 'text')
|
||||||
|
.addColumn('status', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('version', 'integer')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('files_parent_id_index')
|
||||||
|
.on('files')
|
||||||
|
.columns(['parent_id'])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('files').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createFileStatesTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('file_states')
|
||||||
|
.addColumn('file_id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('download_status', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('download_progress', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('download_retries', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('upload_status', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('upload_progress', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('upload_retries', 'integer', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('file_states').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createFileInteractionsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('file_interactions')
|
||||||
|
.addColumn('file_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('collaborator_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('root_id', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('seen_at', 'text')
|
||||||
|
.addColumn('first_opened_at', 'text')
|
||||||
|
.addColumn('last_opened_at', 'text')
|
||||||
|
.addColumn('version', 'integer', (col) => col.notNull())
|
||||||
|
.addPrimaryKeyConstraint('file_interactions_pkey', [
|
||||||
|
'file_id',
|
||||||
|
'collaborator_id',
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('file_interactions').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createMutationsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('mutations')
|
||||||
|
.addColumn('id', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('type', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('data', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('retries', 'integer', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('mutations').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Migration, sql } from 'kysely';
|
||||||
|
|
||||||
|
export const createEntryPathsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('entry_paths')
|
||||||
|
.addColumn('ancestor_id', 'varchar(30)', (col) =>
|
||||||
|
col.notNull().references('entries.id').onDelete('cascade')
|
||||||
|
)
|
||||||
|
.addColumn('descendant_id', 'varchar(30)', (col) =>
|
||||||
|
col.notNull().references('entries.id').onDelete('cascade')
|
||||||
|
)
|
||||||
|
.addColumn('level', 'integer', (col) => col.notNull())
|
||||||
|
.addPrimaryKeyConstraint('entry_paths_pkey', [
|
||||||
|
'ancestor_id',
|
||||||
|
'descendant_id',
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await sql`
|
||||||
|
CREATE TRIGGER trg_insert_entry_path
|
||||||
|
AFTER INSERT ON entries
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
-- Insert direct path from the new entry to itself
|
||||||
|
INSERT INTO entry_paths (ancestor_id, descendant_id, level)
|
||||||
|
VALUES (NEW.id, NEW.id, 0);
|
||||||
|
|
||||||
|
-- Insert paths from ancestors to the new entry
|
||||||
|
INSERT INTO entry_paths (ancestor_id, descendant_id, level)
|
||||||
|
SELECT ancestor_id, NEW.id, level + 1
|
||||||
|
FROM entry_paths
|
||||||
|
WHERE descendant_id = NEW.parent_id AND ancestor_id <> NEW.id;
|
||||||
|
END;
|
||||||
|
`.execute(db);
|
||||||
|
|
||||||
|
await sql`
|
||||||
|
CREATE TRIGGER trg_update_entry_path
|
||||||
|
AFTER UPDATE ON entries
|
||||||
|
FOR EACH ROW
|
||||||
|
WHEN OLD.parent_id <> NEW.parent_id
|
||||||
|
BEGIN
|
||||||
|
-- Delete old paths involving the updated entry
|
||||||
|
DELETE FROM entry_paths
|
||||||
|
WHERE descendant_id = NEW.id AND ancestor_id <> NEW.id;
|
||||||
|
|
||||||
|
-- Insert new paths from ancestors to the updated entry
|
||||||
|
INSERT INTO entry_paths (ancestor_id, descendant_id, level)
|
||||||
|
SELECT ancestor_id, NEW.id, level + 1
|
||||||
|
FROM entry_paths
|
||||||
|
WHERE descendant_id = NEW.parent_id AND ancestor_id <> NEW.id;
|
||||||
|
END;
|
||||||
|
`.execute(db);
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await sql`
|
||||||
|
DROP TRIGGER IF EXISTS trg_insert_entry_path;
|
||||||
|
DROP TRIGGER IF EXISTS trg_update_entry_path;
|
||||||
|
`.execute(db);
|
||||||
|
|
||||||
|
await db.schema.dropTable('entry_paths').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Migration, sql } from 'kysely';
|
||||||
|
|
||||||
|
export const createTextsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await sql`
|
||||||
|
CREATE VIRTUAL TABLE texts USING fts5(id UNINDEXED, name, text);
|
||||||
|
`.execute(db);
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await sql`
|
||||||
|
DROP TABLE IF EXISTS texts;
|
||||||
|
`.execute(db);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
export const createCursorsTable: Migration = {
|
||||||
|
up: async (db) => {
|
||||||
|
await db.schema
|
||||||
|
.createTable('cursors')
|
||||||
|
.addColumn('key', 'text', (col) => col.notNull().primaryKey())
|
||||||
|
.addColumn('value', 'integer', (col) => col.notNull().defaultTo(0))
|
||||||
|
.addColumn('created_at', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('updated_at', 'text')
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
down: async (db) => {
|
||||||
|
await db.schema.dropTable('cursors').execute();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Migration } from 'kysely';
|
||||||
|
|
||||||
|
import { createUsersTable } from './00001-create-users-table';
|
||||||
|
import { createEntriesTable } from './00002-create-entries-table';
|
||||||
|
import { createEntryInteractionsTable } from './00004-create-entry-interactions-table';
|
||||||
|
import { createEntryTransactionsTable } from './00003-create-entry-transactions-table';
|
||||||
|
import { createCollaborationsTable } from './00005-create-collaborations-table';
|
||||||
|
import { createMessagesTable } from './00006-create-messages-table';
|
||||||
|
import { createMessageReactionsTable } from './00007-create-message-reactions-table';
|
||||||
|
import { createMessageInteractionsTable } from './00008-create-message-interactions-table';
|
||||||
|
import { createFilesTable } from './00009-create-files-table';
|
||||||
|
import { createFileStatesTable } from './00010-create-file-states-table';
|
||||||
|
import { createFileInteractionsTable } from './00011-create-file-interactions-table';
|
||||||
|
import { createMutationsTable } from './00012-create-mutations-table';
|
||||||
|
import { createEntryPathsTable } from './00013-create-entry-paths-table';
|
||||||
|
import { createTextsTable } from './00014-create-texts-table';
|
||||||
|
import { createCursorsTable } from './00015-create-cursors-table';
|
||||||
|
|
||||||
|
export const workspaceDatabaseMigrations: Record<string, Migration> = {
|
||||||
|
'00001-create-users-table': createUsersTable,
|
||||||
|
'00002-create-entries-table': createEntriesTable,
|
||||||
|
'00003-create-entry-transactions-table': createEntryTransactionsTable,
|
||||||
|
'00004-create-entry-interactions-table': createEntryInteractionsTable,
|
||||||
|
'00005-create-collaborations-table': createCollaborationsTable,
|
||||||
|
'00006-create-messages-table': createMessagesTable,
|
||||||
|
'00007-create-message-reactions-table': createMessageReactionsTable,
|
||||||
|
'00008-create-message-interactions-table': createMessageInteractionsTable,
|
||||||
|
'00009-create-files-table': createFilesTable,
|
||||||
|
'00010-create-file-states-table': createFileStatesTable,
|
||||||
|
'00011-create-file-interactions-table': createFileInteractionsTable,
|
||||||
|
'00012-create-mutations-table': createMutationsTable,
|
||||||
|
'00013-create-entry-paths-table': createEntryPathsTable,
|
||||||
|
'00014-create-texts-table': createTextsTable,
|
||||||
|
'00015-create-cursors-table': createCursorsTable,
|
||||||
|
};
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type CleanDeletedFilesInput = {
|
|
||||||
type: 'clean_deleted_files';
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
clean_deleted_files: {
|
|
||||||
input: CleanDeletedFilesInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CleanDeletedFilesJobHandler
|
|
||||||
implements JobHandler<CleanDeletedFilesInput>
|
|
||||||
{
|
|
||||||
public triggerDebounce = 1000;
|
|
||||||
public interval = 1000 * 60 * 10;
|
|
||||||
|
|
||||||
public async handleJob(input: CleanDeletedFilesInput) {
|
|
||||||
await fileService.cleanDeletedFiles(input.userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type CleanTempFilesInput = {
|
|
||||||
type: 'clean_temp_files';
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
clean_temp_files: {
|
|
||||||
input: CleanTempFilesInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CleanTempFilesJobHandler
|
|
||||||
implements JobHandler<CleanTempFilesInput>
|
|
||||||
{
|
|
||||||
public triggerDebounce = 1000;
|
|
||||||
public interval = 1000 * 60 * 30;
|
|
||||||
|
|
||||||
public async handleJob(input: CleanTempFilesInput) {
|
|
||||||
await fileService.cleanTempFiles(input.userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { createDebugger } from '@colanode/core';
|
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { socketService } from '@/main/services/socket-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type ConnectSocketInput = {
|
|
||||||
type: 'connect_socket';
|
|
||||||
accountId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
connect_socket: {
|
|
||||||
input: ConnectSocketInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ConnectSocketJobHandler implements JobHandler<ConnectSocketInput> {
|
|
||||||
public triggerDebounce = 0;
|
|
||||||
public interval = 1000 * 30;
|
|
||||||
|
|
||||||
private readonly debug = createDebugger('desktop:job:connect-socket');
|
|
||||||
|
|
||||||
public async handleJob(input: ConnectSocketInput) {
|
|
||||||
const account = await databaseService.appDatabase
|
|
||||||
.selectFrom('accounts')
|
|
||||||
.selectAll()
|
|
||||||
.where('id', '=', input.accountId)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
this.debug(`Account ${input.accountId} not found`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.debug(`Checking connection to socket for account ${account.email}`);
|
|
||||||
socketService.checkConnection(account);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type DownloadFilesInput = {
|
|
||||||
type: 'download_files';
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
download_files: {
|
|
||||||
input: DownloadFilesInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DownloadFilesJobHandler implements JobHandler<DownloadFilesInput> {
|
|
||||||
public triggerDebounce = 0;
|
|
||||||
public interval = 1000 * 60;
|
|
||||||
|
|
||||||
public async handleJob(input: DownloadFilesInput) {
|
|
||||||
await fileService.downloadFiles(input.userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
||||||
export interface JobMap {}
|
|
||||||
|
|
||||||
export type JobInput = JobMap[keyof JobMap]['input'];
|
|
||||||
|
|
||||||
export interface JobHandler<T extends JobInput> {
|
|
||||||
triggerDebounce: number;
|
|
||||||
interval: number;
|
|
||||||
handleJob: (input: T) => Promise<void>;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { syncService } from '@/main/services/sync-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type InitSynchronizersInput = {
|
|
||||||
type: 'init_synchronizers';
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
init_synchronizers: {
|
|
||||||
input: InitSynchronizersInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InitSynchronizersJobHandler
|
|
||||||
implements JobHandler<InitSynchronizersInput>
|
|
||||||
{
|
|
||||||
public triggerDebounce = 100;
|
|
||||||
public interval = 1000 * 60;
|
|
||||||
|
|
||||||
public async handleJob(input: InitSynchronizersInput) {
|
|
||||||
syncService.initSynchronizers(input.userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import { createDebugger } from '@colanode/core';
|
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { messageService } from '@/main/services/message-service';
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
import { mapMutation } from '@/main/utils';
|
|
||||||
|
|
||||||
export type RevertInvalidMutationsInput = {
|
|
||||||
type: 'revert_invalid_mutations';
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
revert_invalid_mutations: {
|
|
||||||
input: RevertInvalidMutationsInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RevertInvalidMutationsJobHandler
|
|
||||||
implements JobHandler<RevertInvalidMutationsInput>
|
|
||||||
{
|
|
||||||
public triggerDebounce = 100;
|
|
||||||
public interval = 1000 * 60 * 5;
|
|
||||||
|
|
||||||
private readonly debug = createDebugger(
|
|
||||||
'desktop:job:revert-invalid-mutations'
|
|
||||||
);
|
|
||||||
|
|
||||||
public async handleJob(input: RevertInvalidMutationsInput) {
|
|
||||||
this.debug(`Reverting invalid mutations for user ${input.userId}`);
|
|
||||||
|
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const invalidMutations = await workspaceDatabase
|
|
||||||
.selectFrom('mutations')
|
|
||||||
.selectAll()
|
|
||||||
.where('retries', '>=', 10)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
if (invalidMutations.length === 0) {
|
|
||||||
this.debug(
|
|
||||||
`No invalid mutations found for user ${input.userId}, skipping`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const mutationRow of invalidMutations) {
|
|
||||||
const mutation = mapMutation(mutationRow);
|
|
||||||
|
|
||||||
if (mutation.type === 'create_file') {
|
|
||||||
await fileService.revertFileCreation(input.userId, mutation.id);
|
|
||||||
} else if (mutation.type === 'delete_file') {
|
|
||||||
await fileService.revertFileDeletion(input.userId, mutation.id);
|
|
||||||
} else if (mutation.type === 'apply_create_transaction') {
|
|
||||||
await entryService.revertCreateTransaction(input.userId, mutation.data);
|
|
||||||
} else if (mutation.type === 'apply_update_transaction') {
|
|
||||||
await entryService.revertUpdateTransaction(input.userId, mutation.data);
|
|
||||||
} else if (mutation.type === 'apply_delete_transaction') {
|
|
||||||
await entryService.revertDeleteTransaction(input.userId, mutation.data);
|
|
||||||
} else if (mutation.type === 'create_message') {
|
|
||||||
await messageService.revertMessageCreation(
|
|
||||||
input.userId,
|
|
||||||
mutation.data.id
|
|
||||||
);
|
|
||||||
} else if (mutation.type === 'delete_message') {
|
|
||||||
await messageService.revertMessageDeletion(
|
|
||||||
input.userId,
|
|
||||||
mutation.data.id
|
|
||||||
);
|
|
||||||
} else if (mutation.type === 'create_message_reaction') {
|
|
||||||
await messageService.revertMessageReactionCreation(
|
|
||||||
input.userId,
|
|
||||||
mutation.data
|
|
||||||
);
|
|
||||||
} else if (mutation.type === 'delete_message_reaction') {
|
|
||||||
await messageService.revertMessageReactionDeletion(
|
|
||||||
input.userId,
|
|
||||||
mutation.data
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mutationIds = invalidMutations.map((m) => m.id);
|
|
||||||
|
|
||||||
await workspaceDatabase
|
|
||||||
.deleteFrom('mutations')
|
|
||||||
.where('id', 'in', mutationIds)
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { accountService } from '@/main/services/account-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type SyncAccountInput = {
|
|
||||||
type: 'sync_account';
|
|
||||||
accountId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
sync_account: {
|
|
||||||
input: SyncAccountInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SyncAccountJobHandler implements JobHandler<SyncAccountInput> {
|
|
||||||
public triggerDebounce = 0;
|
|
||||||
public interval = 1000 * 60;
|
|
||||||
|
|
||||||
public async handleJob(input: SyncAccountInput) {
|
|
||||||
await accountService.syncAccount(input.accountId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import { ApiErrorCode, createDebugger } from '@colanode/core';
|
|
||||||
|
|
||||||
import { serverService } from '@/main/services/server-service';
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
import { httpClient } from '@/shared/lib/http-client';
|
|
||||||
import { parseApiError } from '@/shared/lib/axios';
|
|
||||||
|
|
||||||
export type SyncDeletedTokensInput = {
|
|
||||||
type: 'sync_deleted_tokens';
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
sync_deleted_tokens: {
|
|
||||||
input: SyncDeletedTokensInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SyncDeletedTokensJobHandler
|
|
||||||
implements JobHandler<SyncDeletedTokensInput>
|
|
||||||
{
|
|
||||||
public triggerDebounce = 100;
|
|
||||||
public interval = 1000 * 60 * 5;
|
|
||||||
|
|
||||||
private readonly debug = createDebugger('desktop:job:sync-deleted-tokens');
|
|
||||||
|
|
||||||
public async handleJob(_: SyncDeletedTokensInput) {
|
|
||||||
this.debug('Syncing deleted tokens');
|
|
||||||
|
|
||||||
const deletedTokens = await databaseService.appDatabase
|
|
||||||
.selectFrom('deleted_tokens')
|
|
||||||
.innerJoin('servers', 'deleted_tokens.server', 'servers.domain')
|
|
||||||
.select([
|
|
||||||
'deleted_tokens.token',
|
|
||||||
'deleted_tokens.account_id',
|
|
||||||
'servers.domain',
|
|
||||||
'servers.attributes',
|
|
||||||
])
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
if (deletedTokens.length === 0) {
|
|
||||||
this.debug('No deleted tokens found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const deletedToken of deletedTokens) {
|
|
||||||
if (!serverService.isAvailable(deletedToken.domain)) {
|
|
||||||
this.debug(
|
|
||||||
`Server ${deletedToken.domain} is not available for logging out account ${deletedToken.account_id}`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status } = await httpClient.delete(`/v1/accounts/logout`, {
|
|
||||||
domain: deletedToken.domain,
|
|
||||||
token: deletedToken.token,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.debug(`Deleted token logout response status code: ${status}`);
|
|
||||||
|
|
||||||
if (status !== 200) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await databaseService.appDatabase
|
|
||||||
.deleteFrom('deleted_tokens')
|
|
||||||
.where('token', '=', deletedToken.token)
|
|
||||||
.where('account_id', '=', deletedToken.account_id)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
this.debug(
|
|
||||||
`Logged out account ${deletedToken.account_id} from server ${deletedToken.domain}`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
const parsedError = parseApiError(error);
|
|
||||||
if (
|
|
||||||
parsedError.code === ApiErrorCode.TokenInvalid ||
|
|
||||||
parsedError.code === ApiErrorCode.AccountNotFound ||
|
|
||||||
parsedError.code === ApiErrorCode.DeviceNotFound
|
|
||||||
) {
|
|
||||||
this.debug(
|
|
||||||
`Account ${deletedToken.account_id} is already logged out, skipping...`
|
|
||||||
);
|
|
||||||
|
|
||||||
await databaseService.appDatabase
|
|
||||||
.deleteFrom('deleted_tokens')
|
|
||||||
.where('token', '=', deletedToken.token)
|
|
||||||
.where('account_id', '=', deletedToken.account_id)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.debug(
|
|
||||||
`Failed to logout account ${deletedToken.account_id} from server ${deletedToken.domain}`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { serverService } from '@/main/services/server-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type SyncServersInput = {
|
|
||||||
type: 'sync_servers';
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
sync_servers: {
|
|
||||||
input: SyncServersInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SyncServersJobHandler implements JobHandler<SyncServersInput> {
|
|
||||||
public triggerDebounce = 0;
|
|
||||||
public interval = 1000 * 60;
|
|
||||||
|
|
||||||
public async handleJob(_: SyncServersInput) {
|
|
||||||
await serverService.syncServers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { JobHandler } from '@/main/jobs';
|
|
||||||
|
|
||||||
export type UploadFilesInput = {
|
|
||||||
type: 'upload_files';
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@/main/jobs' {
|
|
||||||
interface JobMap {
|
|
||||||
upload_files: {
|
|
||||||
input: UploadFilesInput;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UploadFilesJobHandler implements JobHandler<UploadFilesInput> {
|
|
||||||
public triggerDebounce = 0;
|
|
||||||
public interval = 1000 * 60;
|
|
||||||
|
|
||||||
public async handleJob(input: UploadFilesInput) {
|
|
||||||
await fileService.uploadFiles(input.userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
24
apps/desktop/src/main/lib/assets.ts
Normal file
24
apps/desktop/src/main/lib/assets.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { getAssetsSourcePath } from '@/main/utils';
|
||||||
|
import { EmojiData } from '@/shared/types/emojis';
|
||||||
|
import { IconData } from '@/shared/types/icons';
|
||||||
|
|
||||||
|
export const getEmojiData = (): EmojiData => {
|
||||||
|
const emojisMetadataPath = path.join(
|
||||||
|
getAssetsSourcePath(),
|
||||||
|
'emojis',
|
||||||
|
'emojis.json'
|
||||||
|
);
|
||||||
|
return JSON.parse(fs.readFileSync(emojisMetadataPath, 'utf8'));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getIconData = (): IconData => {
|
||||||
|
const iconsMetadataPath = path.join(
|
||||||
|
getAssetsSourcePath(),
|
||||||
|
'icons',
|
||||||
|
'icons.json'
|
||||||
|
);
|
||||||
|
return JSON.parse(fs.readFileSync(iconsMetadataPath, 'utf8'));
|
||||||
|
};
|
||||||
102
apps/desktop/src/main/lib/protocols.ts
Normal file
102
apps/desktop/src/main/lib/protocols.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import { net } from 'electron';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getAccountAvatarsDirectoryPath,
|
||||||
|
getAssetsSourcePath,
|
||||||
|
getWorkspaceFilesDirectoryPath,
|
||||||
|
} from '@/main/utils';
|
||||||
|
import { appService } from '@/main/services/app-service';
|
||||||
|
|
||||||
|
export const handleAssetRequest = (request: Request): Promise<Response> => {
|
||||||
|
const url = request.url.replace('asset://', '');
|
||||||
|
const assetPath = path.join(getAssetsSourcePath(), url);
|
||||||
|
const localFileUrl = `file://${assetPath}`;
|
||||||
|
return net.fetch(localFileUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleAvatarRequest = async (
|
||||||
|
request: Request
|
||||||
|
): Promise<Response> => {
|
||||||
|
const url = request.url.replace('avatar://', '');
|
||||||
|
const [accountId, avatarId] = url.split('/');
|
||||||
|
if (!accountId || !avatarId) {
|
||||||
|
return new Response(null, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const avatarsDir = getAccountAvatarsDirectoryPath(accountId);
|
||||||
|
const avatarPath = path.join(avatarsDir, `${avatarId}.jpeg`);
|
||||||
|
const avatarLocalUrl = `file://${avatarPath}`;
|
||||||
|
|
||||||
|
// Check if the avatar file already exists
|
||||||
|
if (fs.existsSync(avatarPath)) {
|
||||||
|
return net.fetch(avatarLocalUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the avatar file if it doesn't exist
|
||||||
|
const account = appService.getAccount(accountId);
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return new Response(null, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await account.client.get<NodeJS.ReadableStream>(
|
||||||
|
`/v1/avatars/${avatarId}`,
|
||||||
|
{
|
||||||
|
responseType: 'stream',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.status !== 200 || !response.data) {
|
||||||
|
return new Response(null, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(avatarsDir)) {
|
||||||
|
fs.mkdirSync(avatarsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileStream = fs.createWriteStream(avatarPath);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
response.data.pipe(fileStream);
|
||||||
|
|
||||||
|
fileStream.on('finish', async () => {
|
||||||
|
resolve(net.fetch(avatarLocalUrl));
|
||||||
|
});
|
||||||
|
|
||||||
|
fileStream.on('error', (err) => {
|
||||||
|
reject(new Response(null, { status: 500, statusText: err.message }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleFilePreviewRequest = async (
|
||||||
|
request: Request
|
||||||
|
): Promise<Response> => {
|
||||||
|
const url = request.url.replace('local-file-preview://', 'file://');
|
||||||
|
return net.fetch(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleFileRequest = async (
|
||||||
|
request: Request
|
||||||
|
): Promise<Response> => {
|
||||||
|
const url = request.url.replace('local-file://', '');
|
||||||
|
const [accountId, workspaceId, file] = url.split('/');
|
||||||
|
if (!accountId || !workspaceId || !file) {
|
||||||
|
return new Response(null, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceFilesDir = getWorkspaceFilesDirectoryPath(
|
||||||
|
accountId,
|
||||||
|
workspaceId
|
||||||
|
);
|
||||||
|
const filePath = path.join(workspaceFilesDir, file);
|
||||||
|
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
const fileUrl = `file://${filePath}`;
|
||||||
|
return net.fetch(fileUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(null, { status: 404 });
|
||||||
|
};
|
||||||
@@ -1,14 +1,29 @@
|
|||||||
import { isEqual } from 'lodash-es';
|
|
||||||
import { createDebugger } from '@colanode/core';
|
import { createDebugger } from '@colanode/core';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
|
import { mutationHandlerMap } from '@/main/mutations';
|
||||||
|
import {
|
||||||
|
MutationHandler,
|
||||||
|
CommandHandler,
|
||||||
|
QueryHandler,
|
||||||
|
SubscribedQuery,
|
||||||
|
} from '@/main/types';
|
||||||
|
import {
|
||||||
|
MutationError,
|
||||||
|
MutationErrorCode,
|
||||||
|
MutationInput,
|
||||||
|
MutationResult,
|
||||||
|
} from '@/shared/mutations';
|
||||||
|
import { commandHandlerMap } from '@/main/commands';
|
||||||
|
import { CommandInput, CommandMap } from '@/shared/commands';
|
||||||
import { queryHandlerMap } from '@/main/queries';
|
import { queryHandlerMap } from '@/main/queries';
|
||||||
import { QueryHandler, SubscribedQuery } from '@/main/types';
|
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { QueryInput, QueryMap } from '@/shared/queries';
|
import { QueryInput, QueryMap } from '@/shared/queries';
|
||||||
import { Event } from '@/shared/types/events';
|
import { Event } from '@/shared/types/events';
|
||||||
|
|
||||||
class QueryService {
|
class Mediator {
|
||||||
private readonly debug = createDebugger('desktop:service:query');
|
private readonly debug = createDebugger('desktop:mediator');
|
||||||
|
|
||||||
private readonly subscribedQueries: Map<string, SubscribedQuery<QueryInput>> =
|
private readonly subscribedQueries: Map<string, SubscribedQuery<QueryInput>> =
|
||||||
new Map();
|
new Map();
|
||||||
|
|
||||||
@@ -127,6 +142,60 @@ class QueryService {
|
|||||||
this.processEventsQueue();
|
this.processEventsQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async executeMutation<T extends MutationInput>(
|
||||||
|
input: T
|
||||||
|
): Promise<MutationResult<T>> {
|
||||||
|
const handler = mutationHandlerMap[
|
||||||
|
input.type
|
||||||
|
] as unknown as MutationHandler<T>;
|
||||||
|
|
||||||
|
this.debug(`Executing mutation: ${input.type}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error(`No handler found for mutation type: ${input.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = await handler.handleMutation(input);
|
||||||
|
return { success: true, output };
|
||||||
|
} catch (error) {
|
||||||
|
this.debug(`Error executing mutation: ${input.type}`, error);
|
||||||
|
if (error instanceof MutationError) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: error.code,
|
||||||
|
message: error.message,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: MutationErrorCode.Unknown,
|
||||||
|
message: 'Something went wrong trying to execute the mutation.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async executeCommand<T extends CommandInput>(
|
||||||
|
input: T
|
||||||
|
): Promise<CommandMap[T['type']]['output']> {
|
||||||
|
this.debug(`Executing command: ${input.type}`);
|
||||||
|
|
||||||
|
const handler = commandHandlerMap[
|
||||||
|
input.type
|
||||||
|
] as unknown as CommandHandler<T>;
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error(`No handler found for command type: ${input.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.handleCommand(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const queryService = new QueryService();
|
export const mediator = new Mediator();
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { databaseService } from '@/main/data/database-service';
|
import { appService } from '@/main/services/app-service';
|
||||||
import { accountService } from '@/main/services/account-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
@@ -13,11 +12,7 @@ export class AccountLogoutMutationHandler
|
|||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: AccountLogoutMutationInput
|
input: AccountLogoutMutationInput
|
||||||
): Promise<AccountLogoutMutationOutput> {
|
): Promise<AccountLogoutMutationOutput> {
|
||||||
const account = await databaseService.appDatabase
|
const account = appService.getAccount(input.accountId);
|
||||||
.selectFrom('accounts')
|
|
||||||
.selectAll()
|
|
||||||
.where('id', '=', input.accountId)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
@@ -26,7 +21,7 @@ export class AccountLogoutMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await accountService.logoutAccount(account);
|
await account.logout();
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { AccountUpdateOutput } from '@colanode/core';
|
import { AccountUpdateOutput } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { httpClient } from '@/shared/lib/http-client';
|
|
||||||
import {
|
import {
|
||||||
AccountUpdateMutationInput,
|
AccountUpdateMutationInput,
|
||||||
AccountUpdateMutationOutput,
|
AccountUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/accounts/account-update';
|
} from '@/shared/mutations/accounts/account-update';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { parseApiError } from '@/shared/lib/axios';
|
import { parseApiError } from '@/shared/lib/axios';
|
||||||
|
import { appService } from '@/main/services/app-service';
|
||||||
|
import { mapAccount } from '@/main/utils';
|
||||||
|
|
||||||
export class AccountUpdateMutationHandler
|
export class AccountUpdateMutationHandler
|
||||||
implements MutationHandler<AccountUpdateMutationInput>
|
implements MutationHandler<AccountUpdateMutationInput>
|
||||||
@@ -17,46 +17,25 @@ export class AccountUpdateMutationHandler
|
|||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: AccountUpdateMutationInput
|
input: AccountUpdateMutationInput
|
||||||
): Promise<AccountUpdateMutationOutput> {
|
): Promise<AccountUpdateMutationOutput> {
|
||||||
const account = await databaseService.appDatabase
|
const accountService = appService.getAccount(input.id);
|
||||||
.selectFrom('accounts')
|
|
||||||
.selectAll()
|
|
||||||
.where('id', '=', input.id)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!account) {
|
if (!accountService) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.AccountNotFound,
|
MutationErrorCode.AccountNotFound,
|
||||||
'Account not found or has been logged out already. Try closing the app and opening it again.'
|
'Account not found or has been logged out already. Try closing the app and opening it again.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = await databaseService.appDatabase
|
|
||||||
.selectFrom('servers')
|
|
||||||
.selectAll()
|
|
||||||
.where('domain', '=', account.server)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!server) {
|
|
||||||
throw new MutationError(
|
|
||||||
MutationErrorCode.ServerNotFound,
|
|
||||||
`The server ${account.server} associated with this account was not found. Try closing the app and opening it again.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await httpClient.put<AccountUpdateOutput>(
|
const { data } = await accountService.client.put<AccountUpdateOutput>(
|
||||||
`/v1/accounts/${input.id}`,
|
`/v1/accounts/${input.id}`,
|
||||||
{
|
{
|
||||||
name: input.name,
|
name: input.name,
|
||||||
avatar: input.avatar,
|
avatar: input.avatar,
|
||||||
},
|
|
||||||
{
|
|
||||||
domain: server.domain,
|
|
||||||
token: account.token,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedAccount = await databaseService.appDatabase
|
const updatedAccount = await appService.database
|
||||||
.updateTable('accounts')
|
.updateTable('accounts')
|
||||||
.set({
|
.set({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -73,17 +52,12 @@ export class AccountUpdateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const account = mapAccount(updatedAccount);
|
||||||
|
accountService.updateAccount(account);
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'account_updated',
|
type: 'account_updated',
|
||||||
account: {
|
account,
|
||||||
id: updatedAccount.id,
|
|
||||||
name: updatedAccount.name,
|
|
||||||
email: updatedAccount.email,
|
|
||||||
token: updatedAccount.token,
|
|
||||||
avatar: updatedAccount.avatar,
|
|
||||||
deviceId: updatedAccount.device_id,
|
|
||||||
server: updatedAccount.server,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
63
apps/desktop/src/main/mutations/accounts/base.ts
Normal file
63
apps/desktop/src/main/mutations/accounts/base.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { LoginSuccessOutput } from '@colanode/core';
|
||||||
|
|
||||||
|
import { appService } from '@/main/services/app-service';
|
||||||
|
import { ServerService } from '@/main/services/server-service';
|
||||||
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { mapAccount, mapWorkspace } from '@/main/utils';
|
||||||
|
|
||||||
|
export abstract class AccountMutationHandlerBase {
|
||||||
|
protected async handleLoginSuccess(
|
||||||
|
login: LoginSuccessOutput,
|
||||||
|
server: ServerService
|
||||||
|
): Promise<void> {
|
||||||
|
const createdAccount = await appService.database
|
||||||
|
.insertInto('accounts')
|
||||||
|
.returningAll()
|
||||||
|
.values({
|
||||||
|
id: login.account.id,
|
||||||
|
email: login.account.email,
|
||||||
|
name: login.account.name,
|
||||||
|
server: server.server.domain,
|
||||||
|
token: login.token,
|
||||||
|
device_id: login.deviceId,
|
||||||
|
avatar: login.account.avatar,
|
||||||
|
})
|
||||||
|
.executeTakeFirst();
|
||||||
|
|
||||||
|
if (!createdAccount) {
|
||||||
|
throw new MutationError(
|
||||||
|
MutationErrorCode.AccountLoginFailed,
|
||||||
|
'Account login failed, please try again.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const account = mapAccount(createdAccount);
|
||||||
|
const accountService = await appService.initAccount(account);
|
||||||
|
|
||||||
|
if (login.workspaces.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const workspace of login.workspaces) {
|
||||||
|
const createdWorkspace = await accountService.database
|
||||||
|
.insertInto('workspaces')
|
||||||
|
.returningAll()
|
||||||
|
.values({
|
||||||
|
id: workspace.id,
|
||||||
|
name: workspace.name,
|
||||||
|
user_id: workspace.user.id,
|
||||||
|
account_id: account.id,
|
||||||
|
role: workspace.user.role,
|
||||||
|
avatar: workspace.avatar,
|
||||||
|
description: workspace.description,
|
||||||
|
})
|
||||||
|
.executeTakeFirst();
|
||||||
|
|
||||||
|
if (!createdWorkspace) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await accountService.initWorkspace(mapWorkspace(createdWorkspace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
import { EmailLoginInput, LoginOutput } from '@colanode/core';
|
import { EmailLoginInput, LoginOutput } from '@colanode/core';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import { app } from 'electron';
|
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { httpClient } from '@/shared/lib/http-client';
|
|
||||||
import { EmailLoginMutationInput } from '@/shared/mutations/accounts/email-login';
|
import { EmailLoginMutationInput } from '@/shared/mutations/accounts/email-login';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { parseApiError } from '@/shared/lib/axios';
|
import { parseApiError } from '@/shared/lib/axios';
|
||||||
import { accountService } from '@/main/services/account-service';
|
import { appService } from '@/main/services/app-service';
|
||||||
|
import { AccountMutationHandlerBase } from '@/main/mutations/accounts/base';
|
||||||
|
|
||||||
export class EmailLoginMutationHandler
|
export class EmailLoginMutationHandler
|
||||||
|
extends AccountMutationHandlerBase
|
||||||
implements MutationHandler<EmailLoginMutationInput>
|
implements MutationHandler<EmailLoginMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(input: EmailLoginMutationInput): Promise<LoginOutput> {
|
async handleMutation(input: EmailLoginMutationInput): Promise<LoginOutput> {
|
||||||
const server = await databaseService.appDatabase
|
const server = appService.getServer(input.server);
|
||||||
.selectFrom('servers')
|
|
||||||
.selectAll()
|
|
||||||
.where('domain', '=', input.server)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
@@ -32,24 +27,23 @@ export class EmailLoginMutationHandler
|
|||||||
email: input.email,
|
email: input.email,
|
||||||
password: input.password,
|
password: input.password,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
version: app.getVersion(),
|
version: appService.version,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await httpClient.post<LoginOutput>(
|
const { data } = await axios.post<LoginOutput>(
|
||||||
'/v1/accounts/emails/login',
|
`${server.apiBaseUrl}/v1/accounts/emails/login`,
|
||||||
emailLoginInput,
|
emailLoginInput
|
||||||
{
|
|
||||||
domain: server.domain,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.type === 'verify') {
|
if (data.type === 'verify') {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
await accountService.initAccount(data, server.domain);
|
await this.handleLoginSuccess(data, server);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
const apiError = parseApiError(error);
|
const apiError = parseApiError(error);
|
||||||
throw new MutationError(MutationErrorCode.ApiError, apiError.message);
|
throw new MutationError(MutationErrorCode.ApiError, apiError.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,21 @@
|
|||||||
import { EmailRegisterInput, LoginOutput } from '@colanode/core';
|
import { EmailRegisterInput, LoginOutput } from '@colanode/core';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import { app } from 'electron';
|
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { httpClient } from '@/shared/lib/http-client';
|
|
||||||
import { EmailRegisterMutationInput } from '@/shared/mutations/accounts/email-register';
|
import { EmailRegisterMutationInput } from '@/shared/mutations/accounts/email-register';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { parseApiError } from '@/shared/lib/axios';
|
import { parseApiError } from '@/shared/lib/axios';
|
||||||
import { accountService } from '@/main/services/account-service';
|
import { appService } from '@/main/services/app-service';
|
||||||
|
import { AccountMutationHandlerBase } from '@/main/mutations/accounts/base';
|
||||||
|
|
||||||
export class EmailRegisterMutationHandler
|
export class EmailRegisterMutationHandler
|
||||||
|
extends AccountMutationHandlerBase
|
||||||
implements MutationHandler<EmailRegisterMutationInput>
|
implements MutationHandler<EmailRegisterMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: EmailRegisterMutationInput
|
input: EmailRegisterMutationInput
|
||||||
): Promise<LoginOutput> {
|
): Promise<LoginOutput> {
|
||||||
const server = await databaseService.appDatabase
|
const server = appService.getServer(input.server);
|
||||||
.selectFrom('servers')
|
|
||||||
.selectAll()
|
|
||||||
.where('domain', '=', input.server)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
@@ -35,22 +30,20 @@ export class EmailRegisterMutationHandler
|
|||||||
email: input.email,
|
email: input.email,
|
||||||
password: input.password,
|
password: input.password,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
version: app.getVersion(),
|
version: appService.version,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await httpClient.post<LoginOutput>(
|
const { data } = await axios.post<LoginOutput>(
|
||||||
'/v1/accounts/emails/register',
|
`${server.apiBaseUrl}/v1/accounts/emails/register`,
|
||||||
emailRegisterInput,
|
emailRegisterInput
|
||||||
{
|
|
||||||
domain: server.domain,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.type === 'verify') {
|
if (data.type === 'verify') {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
await accountService.initAccount(data, server.domain);
|
await this.handleLoginSuccess(data, server);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const apiError = parseApiError(error);
|
const apiError = parseApiError(error);
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
import { EmailVerifyInput, LoginOutput } from '@colanode/core';
|
import { EmailVerifyInput, LoginOutput } from '@colanode/core';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import { app } from 'electron';
|
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { httpClient } from '@/shared/lib/http-client';
|
|
||||||
import { EmailVerifyMutationInput } from '@/shared/mutations/accounts/email-verify';
|
import { EmailVerifyMutationInput } from '@/shared/mutations/accounts/email-verify';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { parseApiError } from '@/shared/lib/axios';
|
import { parseApiError } from '@/shared/lib/axios';
|
||||||
import { accountService } from '@/main/services/account-service';
|
import { appService } from '@/main/services/app-service';
|
||||||
|
import { AccountMutationHandlerBase } from '@/main/mutations/accounts/base';
|
||||||
export class EmailVerifyMutationHandler
|
export class EmailVerifyMutationHandler
|
||||||
|
extends AccountMutationHandlerBase
|
||||||
implements MutationHandler<EmailVerifyMutationInput>
|
implements MutationHandler<EmailVerifyMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(input: EmailVerifyMutationInput): Promise<LoginOutput> {
|
async handleMutation(input: EmailVerifyMutationInput): Promise<LoginOutput> {
|
||||||
const server = await databaseService.appDatabase
|
const server = appService.getServer(input.server);
|
||||||
.selectFrom('servers')
|
|
||||||
.selectAll()
|
|
||||||
.where('domain', '=', input.server)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
@@ -32,15 +26,12 @@ export class EmailVerifyMutationHandler
|
|||||||
id: input.id,
|
id: input.id,
|
||||||
otp: input.otp,
|
otp: input.otp,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
version: app.getVersion(),
|
version: appService.version,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await httpClient.post<LoginOutput>(
|
const { data } = await axios.post<LoginOutput>(
|
||||||
'/v1/accounts/emails/verify',
|
`${server.apiBaseUrl}/v1/accounts/emails/verify`,
|
||||||
emailVerifyInput,
|
emailVerifyInput
|
||||||
{
|
|
||||||
domain: server.domain,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.type === 'verify') {
|
if (data.type === 'verify') {
|
||||||
@@ -50,7 +41,7 @@ export class EmailVerifyMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await accountService.initAccount(data, server.domain);
|
await this.handleLoginSuccess(data, server);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ import FormData from 'form-data';
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { httpClient } from '@/shared/lib/http-client';
|
|
||||||
import {
|
import {
|
||||||
AvatarUploadMutationInput,
|
AvatarUploadMutationInput,
|
||||||
AvatarUploadMutationOutput,
|
AvatarUploadMutationOutput,
|
||||||
} from '@/shared/mutations/avatars/avatar-upload';
|
} from '@/shared/mutations/avatars/avatar-upload';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { parseApiError } from '@/shared/lib/axios';
|
import { parseApiError } from '@/shared/lib/axios';
|
||||||
|
import { appService } from '@/main/services/app-service';
|
||||||
|
|
||||||
interface AvatarUploadResponse {
|
interface AvatarUploadResponse {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -22,14 +21,9 @@ export class AvatarUploadMutationHandler
|
|||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: AvatarUploadMutationInput
|
input: AvatarUploadMutationInput
|
||||||
): Promise<AvatarUploadMutationOutput> {
|
): Promise<AvatarUploadMutationOutput> {
|
||||||
const credentials = await databaseService.appDatabase
|
const account = appService.getAccount(input.accountId);
|
||||||
.selectFrom('accounts')
|
|
||||||
.innerJoin('servers', 'accounts.server', 'servers.domain')
|
|
||||||
.select(['domain', 'attributes', 'token'])
|
|
||||||
.where('id', '=', input.accountId)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!credentials) {
|
if (!account) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.AccountNotFound,
|
MutationErrorCode.AccountNotFound,
|
||||||
'Account not found or has been logged out already. Try closing the app and opening it again.'
|
'Account not found or has been logged out already. Try closing the app and opening it again.'
|
||||||
@@ -43,12 +37,10 @@ export class AvatarUploadMutationHandler
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('avatar', fileStream);
|
formData.append('avatar', fileStream);
|
||||||
|
|
||||||
const { data } = await httpClient.post<AvatarUploadResponse>(
|
const { data } = await account.client.post<AvatarUploadResponse>(
|
||||||
'/v1/avatars',
|
'/v1/avatars',
|
||||||
formData,
|
formData,
|
||||||
{
|
{
|
||||||
domain: credentials.domain,
|
|
||||||
token: credentials.token,
|
|
||||||
headers: formData.getHeaders(),
|
headers: formData.getHeaders(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
import { ChannelAttributes, generateId, IdType } from '@colanode/core';
|
import { ChannelAttributes, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
ChannelCreateMutationInput,
|
ChannelCreateMutationInput,
|
||||||
ChannelCreateMutationOutput,
|
ChannelCreateMutationOutput,
|
||||||
} from '@/shared/mutations/channels/channel-create';
|
} from '@/shared/mutations/channels/channel-create';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ChannelCreateMutationHandler
|
export class ChannelCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ChannelCreateMutationInput>
|
implements MutationHandler<ChannelCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ChannelCreateMutationInput
|
input: ChannelCreateMutationInput
|
||||||
): Promise<ChannelCreateMutationOutput> {
|
): Promise<ChannelCreateMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const space = await workspaceDatabase
|
const space = await workspace.database
|
||||||
.selectFrom('entries')
|
.selectFrom('entries')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.spaceId)
|
.where('id', '=', input.spaceId)
|
||||||
@@ -40,7 +38,7 @@ export class ChannelCreateMutationHandler
|
|||||||
parentId: input.spaceId,
|
parentId: input.spaceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
await entryService.createEntry(input.userId, {
|
await workspace.entries.createEntry({
|
||||||
id,
|
id,
|
||||||
attributes,
|
attributes,
|
||||||
parentId: input.spaceId,
|
parentId: input.spaceId,
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
ChannelDeleteMutationInput,
|
ChannelDeleteMutationInput,
|
||||||
ChannelDeleteMutationOutput,
|
ChannelDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/channels/channel-delete';
|
} from '@/shared/mutations/channels/channel-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ChannelDeleteMutationHandler
|
export class ChannelDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ChannelDeleteMutationInput>
|
implements MutationHandler<ChannelDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ChannelDeleteMutationInput
|
input: ChannelDeleteMutationInput
|
||||||
): Promise<ChannelDeleteMutationOutput> {
|
): Promise<ChannelDeleteMutationOutput> {
|
||||||
await entryService.deleteEntry(input.channelId, input.userId);
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
await workspace.entries.deleteEntry(input.channelId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { ChannelAttributes } from '@colanode/core';
|
import { ChannelAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
ChannelUpdateMutationInput,
|
ChannelUpdateMutationInput,
|
||||||
ChannelUpdateMutationOutput,
|
ChannelUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/channels/channel-update';
|
} from '@/shared/mutations/channels/channel-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ChannelUpdateMutationHandler
|
export class ChannelUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ChannelUpdateMutationInput>
|
implements MutationHandler<ChannelUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ChannelUpdateMutationInput
|
input: ChannelUpdateMutationInput
|
||||||
): Promise<ChannelUpdateMutationOutput> {
|
): Promise<ChannelUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<ChannelAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<ChannelAttributes>(
|
||||||
input.channelId,
|
input.channelId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
attributes.name = input.name;
|
attributes.name = input.name;
|
||||||
attributes.avatar = input.avatar;
|
attributes.avatar = input.avatar;
|
||||||
|
|||||||
@@ -1,37 +1,35 @@
|
|||||||
import { ChatAttributes, generateId, IdType } from '@colanode/core';
|
import { ChatAttributes, generateId, IdType } from '@colanode/core';
|
||||||
import { sql } from 'kysely';
|
import { sql } from 'kysely';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
ChatCreateMutationInput,
|
ChatCreateMutationInput,
|
||||||
ChatCreateMutationOutput,
|
ChatCreateMutationOutput,
|
||||||
} from '@/shared/mutations/chats/chat-create';
|
} from '@/shared/mutations/chats/chat-create';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
interface ChatRow {
|
interface ChatRow {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChatCreateMutationHandler
|
export class ChatCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ChatCreateMutationInput>
|
implements MutationHandler<ChatCreateMutationInput>
|
||||||
{
|
{
|
||||||
public async handleMutation(
|
public async handleMutation(
|
||||||
input: ChatCreateMutationInput
|
input: ChatCreateMutationInput
|
||||||
): Promise<ChatCreateMutationOutput> {
|
): Promise<ChatCreateMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = sql<ChatRow>`
|
const query = sql<ChatRow>`
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM entries
|
FROM entries
|
||||||
WHERE type = 'chat'
|
WHERE type = 'chat'
|
||||||
AND json_extract(attributes, '$.collaborators.${sql.raw(input.userId)}') is not null
|
AND json_extract(attributes, '$.collaborators.${sql.raw(input.userId)}') is not null
|
||||||
AND json_extract(attributes, '$.collaborators.${sql.raw(input.otherUserId)}') is not null
|
AND json_extract(attributes, '$.collaborators.${sql.raw(workspace.userId)}') is not null
|
||||||
`.compile(workspaceDatabase);
|
`.compile(workspace.database);
|
||||||
|
|
||||||
const existingChats = await workspaceDatabase.executeQuery(query);
|
const existingChats = await workspace.database.executeQuery(query);
|
||||||
const chat = existingChats.rows?.[0];
|
const chat = existingChats.rows?.[0];
|
||||||
if (chat) {
|
if (chat) {
|
||||||
return {
|
return {
|
||||||
@@ -44,11 +42,11 @@ export class ChatCreateMutationHandler
|
|||||||
type: 'chat',
|
type: 'chat',
|
||||||
collaborators: {
|
collaborators: {
|
||||||
[input.userId]: 'admin',
|
[input.userId]: 'admin',
|
||||||
[input.otherUserId]: 'admin',
|
[workspace.userId]: 'admin',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await entryService.createEntry(input.userId, {
|
await workspace.entries.createEntry({
|
||||||
id,
|
id,
|
||||||
attributes,
|
attributes,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
|||||||
@@ -5,19 +5,22 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
DatabaseCreateMutationInput,
|
DatabaseCreateMutationInput,
|
||||||
DatabaseCreateMutationOutput,
|
DatabaseCreateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/database-create';
|
} from '@/shared/mutations/databases/database-create';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class DatabaseCreateMutationHandler
|
export class DatabaseCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<DatabaseCreateMutationInput>
|
implements MutationHandler<DatabaseCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: DatabaseCreateMutationInput
|
input: DatabaseCreateMutationInput
|
||||||
): Promise<DatabaseCreateMutationOutput> {
|
): Promise<DatabaseCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
const databaseId = generateId(IdType.Database);
|
const databaseId = generateId(IdType.Database);
|
||||||
const viewId = generateId(IdType.View);
|
const viewId = generateId(IdType.View);
|
||||||
const fieldId = generateId(IdType.Field);
|
const fieldId = generateId(IdType.Field);
|
||||||
@@ -45,7 +48,7 @@ export class DatabaseCreateMutationHandler
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await entryService.createEntry(input.userId, {
|
await workspace.entries.createEntry({
|
||||||
id: databaseId,
|
id: databaseId,
|
||||||
attributes,
|
attributes,
|
||||||
parentId: input.spaceId,
|
parentId: input.spaceId,
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
DatabaseDeleteMutationInput,
|
DatabaseDeleteMutationInput,
|
||||||
DatabaseDeleteMutationOutput,
|
DatabaseDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/databases/database-delete';
|
} from '@/shared/mutations/databases/database-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class DatabaseDeleteMutationHandler
|
export class DatabaseDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<DatabaseDeleteMutationInput>
|
implements MutationHandler<DatabaseDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: DatabaseDeleteMutationInput
|
input: DatabaseDeleteMutationInput
|
||||||
): Promise<DatabaseDeleteMutationOutput> {
|
): Promise<DatabaseDeleteMutationOutput> {
|
||||||
await entryService.deleteEntry(input.databaseId, input.userId);
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
await workspace.entries.deleteEntry(input.databaseId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
DatabaseUpdateMutationInput,
|
DatabaseUpdateMutationInput,
|
||||||
DatabaseUpdateMutationOutput,
|
DatabaseUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/database-update';
|
} from '@/shared/mutations/databases/database-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class DatabaseUpdateMutationHandler
|
export class DatabaseUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<DatabaseUpdateMutationInput>
|
implements MutationHandler<DatabaseUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: DatabaseUpdateMutationInput
|
input: DatabaseUpdateMutationInput
|
||||||
): Promise<DatabaseUpdateMutationOutput> {
|
): Promise<DatabaseUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
attributes.name = input.name;
|
attributes.name = input.name;
|
||||||
attributes.avatar = input.avatar;
|
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,22 +7,24 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FieldCreateMutationInput,
|
FieldCreateMutationInput,
|
||||||
FieldCreateMutationOutput,
|
FieldCreateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/field-create';
|
} from '@/shared/mutations/databases/field-create';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { fetchEntry } from '@/main/utils';
|
import { fetchEntry } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FieldCreateMutationHandler
|
export class FieldCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FieldCreateMutationInput>
|
implements MutationHandler<FieldCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FieldCreateMutationInput
|
input: FieldCreateMutationInput
|
||||||
): Promise<FieldCreateMutationOutput> {
|
): Promise<FieldCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
if (input.fieldType === 'relation') {
|
if (input.fieldType === 'relation') {
|
||||||
if (!input.relationDatabaseId) {
|
if (!input.relationDatabaseId) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
@@ -31,12 +33,8 @@ export class FieldCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const relationDatabase = await fetchEntry(
|
const relationDatabase = await fetchEntry(
|
||||||
workspaceDatabase,
|
workspace.database,
|
||||||
input.relationDatabaseId
|
input.relationDatabaseId
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -49,9 +47,8 @@ export class FieldCreateMutationHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fieldId = generateId(IdType.Field);
|
const fieldId = generateId(IdType.Field);
|
||||||
const result = await entryService.updateEntry(
|
const result = await workspace.entries.updateEntry(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
if (attributes.type !== 'database') {
|
if (attributes.type !== 'database') {
|
||||||
throw new Error('Invalid node type');
|
throw new Error('Invalid node type');
|
||||||
|
|||||||
@@ -1,30 +1,25 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
FieldDeleteMutationInput,
|
FieldDeleteMutationInput,
|
||||||
FieldDeleteMutationOutput,
|
FieldDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/databases/field-delete';
|
} from '@/shared/mutations/databases/field-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FieldDeleteMutationHandler
|
export class FieldDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FieldDeleteMutationInput>
|
implements MutationHandler<FieldDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FieldDeleteMutationInput
|
input: FieldDeleteMutationInput
|
||||||
): Promise<FieldDeleteMutationOutput> {
|
): Promise<FieldDeleteMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.databaseId,
|
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
|
||||||
if (!attributes.fields[input.fieldId]) {
|
|
||||||
throw new MutationError(
|
|
||||||
MutationErrorCode.FieldNotFound,
|
|
||||||
'The field you are trying to delete does not exist.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
|
input.databaseId,
|
||||||
|
(attributes) => {
|
||||||
delete attributes.fields[input.fieldId];
|
delete attributes.fields[input.fieldId];
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
FieldNameUpdateMutationInput,
|
FieldNameUpdateMutationInput,
|
||||||
FieldNameUpdateMutationOutput,
|
FieldNameUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/field-name-update';
|
} from '@/shared/mutations/databases/field-name-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FieldNameUpdateMutationHandler
|
export class FieldNameUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FieldNameUpdateMutationInput>
|
implements MutationHandler<FieldNameUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FieldNameUpdateMutationInput
|
input: FieldNameUpdateMutationInput
|
||||||
): Promise<FieldNameUpdateMutationOutput> {
|
): Promise<FieldNameUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const field = attributes.fields[input.fieldId];
|
const field = attributes.fields[input.fieldId];
|
||||||
if (!field) {
|
if (!field) {
|
||||||
|
|||||||
@@ -6,24 +6,26 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
SelectOptionCreateMutationInput,
|
SelectOptionCreateMutationInput,
|
||||||
SelectOptionCreateMutationOutput,
|
SelectOptionCreateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/select-option-create';
|
} from '@/shared/mutations/databases/select-option-create';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class SelectOptionCreateMutationHandler
|
export class SelectOptionCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<SelectOptionCreateMutationInput>
|
implements MutationHandler<SelectOptionCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: SelectOptionCreateMutationInput
|
input: SelectOptionCreateMutationInput
|
||||||
): Promise<SelectOptionCreateMutationOutput> {
|
): Promise<SelectOptionCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
const id = generateId(IdType.SelectOption);
|
const id = generateId(IdType.SelectOption);
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const field = attributes.fields[input.fieldId];
|
const field = attributes.fields[input.fieldId];
|
||||||
if (!field) {
|
if (!field) {
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
SelectOptionDeleteMutationInput,
|
SelectOptionDeleteMutationInput,
|
||||||
SelectOptionDeleteMutationOutput,
|
SelectOptionDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/databases/select-option-delete';
|
} from '@/shared/mutations/databases/select-option-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class SelectOptionDeleteMutationHandler
|
export class SelectOptionDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<SelectOptionDeleteMutationInput>
|
implements MutationHandler<SelectOptionDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: SelectOptionDeleteMutationInput
|
input: SelectOptionDeleteMutationInput
|
||||||
): Promise<SelectOptionDeleteMutationOutput> {
|
): Promise<SelectOptionDeleteMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const field = attributes.fields[input.fieldId];
|
const field = attributes.fields[input.fieldId];
|
||||||
if (!field) {
|
if (!field) {
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
SelectOptionUpdateMutationInput,
|
SelectOptionUpdateMutationInput,
|
||||||
SelectOptionUpdateMutationOutput,
|
SelectOptionUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/select-option-update';
|
} from '@/shared/mutations/databases/select-option-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class SelectOptionUpdateMutationHandler
|
export class SelectOptionUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<SelectOptionUpdateMutationInput>
|
implements MutationHandler<SelectOptionUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: SelectOptionUpdateMutationInput
|
input: SelectOptionUpdateMutationInput
|
||||||
): Promise<SelectOptionUpdateMutationOutput> {
|
): Promise<SelectOptionUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const field = attributes.fields[input.fieldId];
|
const field = attributes.fields[input.fieldId];
|
||||||
if (!field) {
|
if (!field) {
|
||||||
|
|||||||
@@ -6,24 +6,26 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
ViewCreateMutationInput,
|
ViewCreateMutationInput,
|
||||||
ViewCreateMutationOutput,
|
ViewCreateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/view-create';
|
} from '@/shared/mutations/databases/view-create';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ViewCreateMutationHandler
|
export class ViewCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ViewCreateMutationInput>
|
implements MutationHandler<ViewCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ViewCreateMutationInput
|
input: ViewCreateMutationInput
|
||||||
): Promise<ViewCreateMutationOutput> {
|
): Promise<ViewCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
const id = generateId(IdType.View);
|
const id = generateId(IdType.View);
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const maxIndex = Object.values(attributes.views)
|
const maxIndex = Object.values(attributes.views)
|
||||||
.map((view) => view.index)
|
.map((view) => view.index)
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
ViewDeleteMutationInput,
|
ViewDeleteMutationInput,
|
||||||
ViewDeleteMutationOutput,
|
ViewDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/databases/view-delete';
|
} from '@/shared/mutations/databases/view-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ViewDeleteMutationHandler
|
export class ViewDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ViewDeleteMutationInput>
|
implements MutationHandler<ViewDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ViewDeleteMutationInput
|
input: ViewDeleteMutationInput
|
||||||
): Promise<ViewDeleteMutationOutput> {
|
): Promise<ViewDeleteMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
if (!attributes.views[input.viewId]) {
|
if (!attributes.views[input.viewId]) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
ViewNameUpdateMutationInput,
|
ViewNameUpdateMutationInput,
|
||||||
ViewNameUpdateMutationOutput,
|
ViewNameUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/view-name-update';
|
} from '@/shared/mutations/databases/view-name-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ViewNameUpdateMutationHandler
|
export class ViewNameUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ViewNameUpdateMutationInput>
|
implements MutationHandler<ViewNameUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ViewNameUpdateMutationInput
|
input: ViewNameUpdateMutationInput
|
||||||
): Promise<ViewNameUpdateMutationOutput> {
|
): Promise<ViewNameUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const view = attributes.views[input.viewId];
|
const view = attributes.views[input.viewId];
|
||||||
if (!view) {
|
if (!view) {
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { DatabaseAttributes } from '@colanode/core';
|
import { DatabaseAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
ViewUpdateMutationInput,
|
ViewUpdateMutationInput,
|
||||||
ViewUpdateMutationOutput,
|
ViewUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/databases/view-update';
|
} from '@/shared/mutations/databases/view-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class ViewUpdateMutationHandler
|
export class ViewUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<ViewUpdateMutationInput>
|
implements MutationHandler<ViewUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ViewUpdateMutationInput
|
input: ViewUpdateMutationInput
|
||||||
): Promise<ViewUpdateMutationOutput> {
|
): Promise<ViewUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<DatabaseAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<DatabaseAttributes>(
|
||||||
input.databaseId,
|
input.databaseId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
if (!attributes.views[input.view.id]) {
|
if (!attributes.views[input.view.id]) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { set } from 'lodash-es';
|
import { set } from 'lodash-es';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
EntryCollaboratorCreateMutationInput,
|
EntryCollaboratorCreateMutationInput,
|
||||||
EntryCollaboratorCreateMutationOutput,
|
EntryCollaboratorCreateMutationOutput,
|
||||||
} from '@/shared/mutations/entries/entry-collaborator-create';
|
} from '@/shared/mutations/entries/entry-collaborator-create';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class EntryCollaboratorCreateMutationHandler
|
export class EntryCollaboratorCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<EntryCollaboratorCreateMutationInput>
|
implements MutationHandler<EntryCollaboratorCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: EntryCollaboratorCreateMutationInput
|
input: EntryCollaboratorCreateMutationInput
|
||||||
): Promise<EntryCollaboratorCreateMutationOutput> {
|
): Promise<EntryCollaboratorCreateMutationOutput> {
|
||||||
const result = await entryService.updateEntry(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry(
|
||||||
input.entryId,
|
input.entryId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
for (const collaboratorId of input.collaboratorIds) {
|
for (const collaboratorId of input.collaboratorIds) {
|
||||||
set(attributes, `collaborators.${collaboratorId}`, input.role);
|
set(attributes, `collaborators.${collaboratorId}`, input.role);
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { unset } from 'lodash-es';
|
import { unset } from 'lodash-es';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
EntryCollaboratorDeleteMutationInput,
|
EntryCollaboratorDeleteMutationInput,
|
||||||
EntryCollaboratorDeleteMutationOutput,
|
EntryCollaboratorDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/entries/entry-collaborator-delete';
|
} from '@/shared/mutations/entries/entry-collaborator-delete';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class EntryCollaboratorDeleteMutationHandler
|
export class EntryCollaboratorDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<EntryCollaboratorDeleteMutationInput>
|
implements MutationHandler<EntryCollaboratorDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: EntryCollaboratorDeleteMutationInput
|
input: EntryCollaboratorDeleteMutationInput
|
||||||
): Promise<EntryCollaboratorDeleteMutationOutput> {
|
): Promise<EntryCollaboratorDeleteMutationOutput> {
|
||||||
const result = await entryService.updateEntry(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry(
|
||||||
input.entryId,
|
input.entryId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
unset(attributes, `collaborators.${input.collaboratorId}`);
|
unset(attributes, `collaborators.${input.collaboratorId}`);
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { set } from 'lodash-es';
|
import { set } from 'lodash-es';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
EntryCollaboratorUpdateMutationInput,
|
EntryCollaboratorUpdateMutationInput,
|
||||||
EntryCollaboratorUpdateMutationOutput,
|
EntryCollaboratorUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/entries/entry-collaborator-update';
|
} from '@/shared/mutations/entries/entry-collaborator-update';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class EntryCollaboratorUpdateMutationHandler
|
export class EntryCollaboratorUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<EntryCollaboratorUpdateMutationInput>
|
implements MutationHandler<EntryCollaboratorUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: EntryCollaboratorUpdateMutationInput
|
input: EntryCollaboratorUpdateMutationInput
|
||||||
): Promise<EntryCollaboratorUpdateMutationOutput> {
|
): Promise<EntryCollaboratorUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry(
|
||||||
input.entryId,
|
input.entryId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
set(attributes, `collaborators.${input.collaboratorId}`, input.role);
|
set(attributes, `collaborators.${input.collaboratorId}`, input.role);
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
import { MarkEntryOpenedMutation, generateId, IdType } from '@colanode/core';
|
import { MarkEntryOpenedMutation, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
EntryMarkOpenedMutationInput,
|
EntryMarkOpenedMutationInput,
|
||||||
EntryMarkOpenedMutationOutput,
|
EntryMarkOpenedMutationOutput,
|
||||||
} from '@/shared/mutations/entries/entry-mark-opened';
|
} from '@/shared/mutations/entries/entry-mark-opened';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { mapEntryInteraction } from '@/main/utils';
|
import { fetchEntry, mapEntryInteraction } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class EntryMarkOpenedMutationHandler
|
export class EntryMarkOpenedMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<EntryMarkOpenedMutationInput>
|
implements MutationHandler<EntryMarkOpenedMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: EntryMarkOpenedMutationInput
|
input: EntryMarkOpenedMutationInput
|
||||||
): Promise<EntryMarkOpenedMutationOutput> {
|
): Promise<EntryMarkOpenedMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const entry = await workspaceDatabase
|
const entry = await fetchEntry(workspace.database, input.entryId);
|
||||||
.selectFrom('entries')
|
|
||||||
.selectAll()
|
|
||||||
.where('id', '=', input.entryId)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return {
|
return {
|
||||||
@@ -31,11 +26,11 @@ export class EntryMarkOpenedMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingInteraction = await workspaceDatabase
|
const existingInteraction = await workspace.database
|
||||||
.selectFrom('entry_interactions')
|
.selectFrom('entry_interactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('entry_id', '=', input.entryId)
|
.where('entry_id', '=', input.entryId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (existingInteraction) {
|
if (existingInteraction) {
|
||||||
@@ -55,7 +50,7 @@ export class EntryMarkOpenedMutationHandler
|
|||||||
? existingInteraction.first_opened_at
|
? existingInteraction.first_opened_at
|
||||||
: lastOpenedAt;
|
: lastOpenedAt;
|
||||||
|
|
||||||
const { createdInteraction, createdMutation } = await workspaceDatabase
|
const { createdInteraction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const createdInteraction = await trx
|
const createdInteraction = await trx
|
||||||
@@ -63,7 +58,7 @@ export class EntryMarkOpenedMutationHandler
|
|||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
entry_id: input.entryId,
|
entry_id: input.entryId,
|
||||||
collaborator_id: input.userId,
|
collaborator_id: workspace.userId,
|
||||||
last_opened_at: lastOpenedAt,
|
last_opened_at: lastOpenedAt,
|
||||||
first_opened_at: firstOpenedAt,
|
first_opened_at: firstOpenedAt,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
@@ -87,7 +82,7 @@ export class EntryMarkOpenedMutationHandler
|
|||||||
type: 'mark_entry_opened',
|
type: 'mark_entry_opened',
|
||||||
data: {
|
data: {
|
||||||
entryId: input.entryId,
|
entryId: input.entryId,
|
||||||
collaboratorId: input.userId,
|
collaboratorId: workspace.userId,
|
||||||
openedAt: new Date().toISOString(),
|
openedAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -114,14 +109,12 @@ export class EntryMarkOpenedMutationHandler
|
|||||||
throw new Error('Failed to create entry interaction');
|
throw new Error('Failed to create entry interaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'entry_interaction_updated',
|
type: 'entry_interaction_updated',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
entryInteraction: mapEntryInteraction(createdInteraction),
|
entryInteraction: mapEntryInteraction(createdInteraction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
import { MarkEntrySeenMutation, generateId, IdType } from '@colanode/core';
|
import { MarkEntrySeenMutation, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
EntryMarkSeenMutationInput,
|
EntryMarkSeenMutationInput,
|
||||||
EntryMarkSeenMutationOutput,
|
EntryMarkSeenMutationOutput,
|
||||||
} from '@/shared/mutations/entries/entry-mark-seen';
|
} from '@/shared/mutations/entries/entry-mark-seen';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { mapEntryInteraction } from '@/main/utils';
|
import { fetchEntry, mapEntryInteraction } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class EntryMarkSeenMutationHandler
|
export class EntryMarkSeenMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<EntryMarkSeenMutationInput>
|
implements MutationHandler<EntryMarkSeenMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: EntryMarkSeenMutationInput
|
input: EntryMarkSeenMutationInput
|
||||||
): Promise<EntryMarkSeenMutationOutput> {
|
): Promise<EntryMarkSeenMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const entry = await workspaceDatabase
|
const entry = await fetchEntry(workspace.database, input.entryId);
|
||||||
.selectFrom('entries')
|
|
||||||
.selectAll()
|
|
||||||
.where('id', '=', input.entryId)
|
|
||||||
.executeTakeFirst();
|
|
||||||
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return {
|
return {
|
||||||
@@ -31,11 +26,11 @@ export class EntryMarkSeenMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingInteraction = await workspaceDatabase
|
const existingInteraction = await workspace.database
|
||||||
.selectFrom('entry_interactions')
|
.selectFrom('entry_interactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('entry_id', '=', input.entryId)
|
.where('entry_id', '=', input.entryId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (existingInteraction) {
|
if (existingInteraction) {
|
||||||
@@ -55,7 +50,7 @@ export class EntryMarkSeenMutationHandler
|
|||||||
? existingInteraction.first_seen_at
|
? existingInteraction.first_seen_at
|
||||||
: lastSeenAt;
|
: lastSeenAt;
|
||||||
|
|
||||||
const { createdInteraction, createdMutation } = await workspaceDatabase
|
const { createdInteraction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const createdInteraction = await trx
|
const createdInteraction = await trx
|
||||||
@@ -63,7 +58,7 @@ export class EntryMarkSeenMutationHandler
|
|||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
entry_id: input.entryId,
|
entry_id: input.entryId,
|
||||||
collaborator_id: input.userId,
|
collaborator_id: workspace.userId,
|
||||||
last_seen_at: lastSeenAt,
|
last_seen_at: lastSeenAt,
|
||||||
first_seen_at: firstSeenAt,
|
first_seen_at: firstSeenAt,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
@@ -87,7 +82,7 @@ export class EntryMarkSeenMutationHandler
|
|||||||
type: 'mark_entry_seen',
|
type: 'mark_entry_seen',
|
||||||
data: {
|
data: {
|
||||||
entryId: input.entryId,
|
entryId: input.entryId,
|
||||||
collaboratorId: input.userId,
|
collaboratorId: workspace.userId,
|
||||||
seenAt: new Date().toISOString(),
|
seenAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -114,14 +109,12 @@ export class EntryMarkSeenMutationHandler
|
|||||||
throw new Error('Failed to create entry interaction');
|
throw new Error('Failed to create entry interaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'entry_interaction_updated',
|
type: 'entry_interaction_updated',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
entryInteraction: mapEntryInteraction(createdInteraction),
|
entryInteraction: mapEntryInteraction(createdInteraction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,32 +6,33 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FileCreateMutationInput,
|
FileCreateMutationInput,
|
||||||
FileCreateMutationOutput,
|
FileCreateMutationOutput,
|
||||||
} from '@/shared/mutations/files/file-create';
|
} from '@/shared/mutations/files/file-create';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import {
|
import {
|
||||||
fetchEntry,
|
fetchEntry,
|
||||||
fetchUser,
|
fetchUser,
|
||||||
fetchUserStorageUsed,
|
fetchUserStorageUsed,
|
||||||
|
getFileMetadata,
|
||||||
mapEntry,
|
mapEntry,
|
||||||
mapFile,
|
mapFile,
|
||||||
} from '@/main/utils';
|
} from '@/main/utils';
|
||||||
import { formatBytes } from '@/shared/lib/files';
|
import { formatBytes } from '@/shared/lib/files';
|
||||||
import { DownloadStatus, UploadStatus } from '@/shared/types/files';
|
import { DownloadStatus, UploadStatus } from '@/shared/types/files';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FileCreateMutationHandler
|
export class FileCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FileCreateMutationInput>
|
implements MutationHandler<FileCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FileCreateMutationInput
|
input: FileCreateMutationInput
|
||||||
): Promise<FileCreateMutationOutput> {
|
): Promise<FileCreateMutationOutput> {
|
||||||
const metadata = fileService.getFileMetadata(input.filePath);
|
const metadata = getFileMetadata(input.filePath);
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.FileInvalid,
|
MutationErrorCode.FileInvalid,
|
||||||
@@ -39,11 +40,10 @@ export class FileCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
const user = await fetchUser(workspace.database, workspace.userId);
|
||||||
|
|
||||||
const user = await fetchUser(workspaceDatabase, input.userId);
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.UserNotFound,
|
MutationErrorCode.UserNotFound,
|
||||||
@@ -60,8 +60,8 @@ export class FileCreateMutationHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
const storageUsed = await fetchUserStorageUsed(
|
const storageUsed = await fetchUserStorageUsed(
|
||||||
workspaceDatabase,
|
workspace.database,
|
||||||
input.userId
|
workspace.userId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (storageUsed + BigInt(metadata.size) > user.storage_limit) {
|
if (storageUsed + BigInt(metadata.size) > user.storage_limit) {
|
||||||
@@ -76,7 +76,7 @@ export class FileCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await fetchEntry(workspaceDatabase, input.entryId);
|
const entry = await fetchEntry(workspace.database, input.entryId);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.EntryNotFound,
|
MutationErrorCode.EntryNotFound,
|
||||||
@@ -84,7 +84,7 @@ export class FileCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await fetchEntry(workspaceDatabase, input.rootId);
|
const root = await fetchEntry(workspace.database, input.rootId);
|
||||||
if (!root) {
|
if (!root) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.RootNotFound,
|
MutationErrorCode.RootNotFound,
|
||||||
@@ -96,8 +96,8 @@ export class FileCreateMutationHandler
|
|||||||
if (
|
if (
|
||||||
!canCreateFile({
|
!canCreateFile({
|
||||||
user: {
|
user: {
|
||||||
userId: input.userId,
|
userId: workspace.userId,
|
||||||
role: user.role,
|
role: workspace.role,
|
||||||
},
|
},
|
||||||
root: mapEntry(root),
|
root: mapEntry(root),
|
||||||
entry: mapEntry(entry),
|
entry: mapEntry(entry),
|
||||||
@@ -113,11 +113,10 @@ export class FileCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fileService.copyFileToWorkspace(
|
workspace.files.copyFileToWorkspace(
|
||||||
input.filePath,
|
input.filePath,
|
||||||
fileId,
|
fileId,
|
||||||
metadata.extension,
|
metadata.extension
|
||||||
input.userId
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const mutationData: CreateFileMutationData = {
|
const mutationData: CreateFileMutationData = {
|
||||||
@@ -134,7 +133,7 @@ export class FileCreateMutationHandler
|
|||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const createdFile = await workspaceDatabase
|
const createdFile = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (tx) => {
|
.execute(async (tx) => {
|
||||||
const createdFile = await tx
|
const createdFile = await tx
|
||||||
@@ -152,7 +151,7 @@ export class FileCreateMutationHandler
|
|||||||
size: metadata.size,
|
size: metadata.size,
|
||||||
extension: metadata.extension,
|
extension: metadata.extension,
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
created_by: input.userId,
|
created_by: workspace.userId,
|
||||||
status: FileStatus.Pending,
|
status: FileStatus.Pending,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
})
|
})
|
||||||
@@ -196,18 +195,17 @@ export class FileCreateMutationHandler
|
|||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_created',
|
type: 'file_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
file: mapFile(createdFile),
|
file: mapFile(createdFile),
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_state_created',
|
type: 'file_state_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
fileState: {
|
fileState: {
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
downloadProgress: 100,
|
downloadProgress: 100,
|
||||||
|
|||||||
@@ -5,27 +5,26 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FileDeleteMutationInput,
|
FileDeleteMutationInput,
|
||||||
FileDeleteMutationOutput,
|
FileDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/files/file-delete';
|
} from '@/shared/mutations/files/file-delete';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { fetchEntry, fetchUser, mapEntry, mapFile } from '@/main/utils';
|
import { fetchEntry, mapEntry, mapFile } from '@/main/utils';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FileDeleteMutationHandler
|
export class FileDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FileDeleteMutationInput>
|
implements MutationHandler<FileDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FileDeleteMutationInput
|
input: FileDeleteMutationInput
|
||||||
): Promise<FileDeleteMutationOutput> {
|
): Promise<FileDeleteMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const file = await workspaceDatabase
|
const file = await workspace.database
|
||||||
.selectFrom('files')
|
.selectFrom('files')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.fileId)
|
.where('id', '=', input.fileId)
|
||||||
@@ -38,15 +37,7 @@ export class FileDeleteMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await fetchUser(workspaceDatabase, input.userId);
|
const entry = await fetchEntry(workspace.database, file.root_id);
|
||||||
if (!user) {
|
|
||||||
throw new MutationError(
|
|
||||||
MutationErrorCode.UserNotFound,
|
|
||||||
'There was an error while fetching the user. Please make sure you are logged in.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = await fetchEntry(workspaceDatabase, file.root_id);
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.EntryNotFound,
|
MutationErrorCode.EntryNotFound,
|
||||||
@@ -54,7 +45,7 @@ export class FileDeleteMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await fetchEntry(workspaceDatabase, entry.root_id);
|
const root = await fetchEntry(workspace.database, entry.root_id);
|
||||||
if (!root) {
|
if (!root) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.RootNotFound,
|
MutationErrorCode.RootNotFound,
|
||||||
@@ -65,8 +56,8 @@ export class FileDeleteMutationHandler
|
|||||||
if (
|
if (
|
||||||
!canDeleteFile({
|
!canDeleteFile({
|
||||||
user: {
|
user: {
|
||||||
userId: input.userId,
|
userId: workspace.userId,
|
||||||
role: user.role,
|
role: workspace.role,
|
||||||
},
|
},
|
||||||
root: mapEntry(root),
|
root: mapEntry(root),
|
||||||
entry: mapEntry(entry),
|
entry: mapEntry(entry),
|
||||||
@@ -90,7 +81,7 @@ export class FileDeleteMutationHandler
|
|||||||
deletedAt,
|
deletedAt,
|
||||||
};
|
};
|
||||||
|
|
||||||
await workspaceDatabase.transaction().execute(async (tx) => {
|
await workspace.database.transaction().execute(async (tx) => {
|
||||||
await tx
|
await tx
|
||||||
.updateTable('files')
|
.updateTable('files')
|
||||||
.set({
|
.set({
|
||||||
@@ -113,14 +104,12 @@ export class FileDeleteMutationHandler
|
|||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_deleted',
|
type: 'file_deleted',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
file: mapFile(file),
|
file: mapFile(file),
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { FileStatus } from '@colanode/core';
|
import { FileStatus } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { mapFileState } from '@/main/utils';
|
import { mapFileState } from '@/main/utils';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
@@ -10,18 +9,18 @@ import {
|
|||||||
FileDownloadMutationOutput,
|
FileDownloadMutationOutput,
|
||||||
} from '@/shared/mutations/files/file-download';
|
} from '@/shared/mutations/files/file-download';
|
||||||
import { DownloadStatus, UploadStatus } from '@/shared/types/files';
|
import { DownloadStatus, UploadStatus } from '@/shared/types/files';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FileDownloadMutationHandler
|
export class FileDownloadMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FileDownloadMutationInput>
|
implements MutationHandler<FileDownloadMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FileDownloadMutationInput
|
input: FileDownloadMutationInput
|
||||||
): Promise<FileDownloadMutationOutput> {
|
): Promise<FileDownloadMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const file = await workspaceDatabase
|
const file = await workspace.database
|
||||||
.selectFrom('files')
|
.selectFrom('files')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.fileId)
|
.where('id', '=', input.fileId)
|
||||||
@@ -41,7 +40,7 @@ export class FileDownloadMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingFileState = await workspaceDatabase
|
const existingFileState = await workspace.database
|
||||||
.selectFrom('file_states')
|
.selectFrom('file_states')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('file_id', '=', input.fileId)
|
.where('file_id', '=', input.fileId)
|
||||||
@@ -56,7 +55,7 @@ export class FileDownloadMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileState = await workspaceDatabase
|
const fileState = await workspace.database
|
||||||
.insertInto('file_states')
|
.insertInto('file_states')
|
||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
@@ -85,7 +84,8 @@ export class FileDownloadMutationHandler
|
|||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_state_created',
|
type: 'file_state_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
fileState: mapFileState(fileState),
|
fileState: mapFileState(fileState),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { MarkFileOpenedMutation, generateId, IdType } from '@colanode/core';
|
import { MarkFileOpenedMutation, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FileMarkOpenedMutationInput,
|
FileMarkOpenedMutationInput,
|
||||||
@@ -8,18 +7,18 @@ import {
|
|||||||
} from '@/shared/mutations/files/file-mark-opened';
|
} from '@/shared/mutations/files/file-mark-opened';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { mapFileInteraction } from '@/main/utils';
|
import { mapFileInteraction } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FileMarkOpenedMutationHandler
|
export class FileMarkOpenedMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FileMarkOpenedMutationInput>
|
implements MutationHandler<FileMarkOpenedMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FileMarkOpenedMutationInput
|
input: FileMarkOpenedMutationInput
|
||||||
): Promise<FileMarkOpenedMutationOutput> {
|
): Promise<FileMarkOpenedMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const file = await workspaceDatabase
|
const file = await workspace.database
|
||||||
.selectFrom('files')
|
.selectFrom('files')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.fileId)
|
.where('id', '=', input.fileId)
|
||||||
@@ -31,11 +30,11 @@ export class FileMarkOpenedMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingInteraction = await workspaceDatabase
|
const existingInteraction = await workspace.database
|
||||||
.selectFrom('file_interactions')
|
.selectFrom('file_interactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('file_id', '=', input.fileId)
|
.where('file_id', '=', input.fileId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (existingInteraction) {
|
if (existingInteraction) {
|
||||||
@@ -55,7 +54,7 @@ export class FileMarkOpenedMutationHandler
|
|||||||
? existingInteraction.first_opened_at
|
? existingInteraction.first_opened_at
|
||||||
: lastOpenedAt;
|
: lastOpenedAt;
|
||||||
|
|
||||||
const { createdInteraction, createdMutation } = await workspaceDatabase
|
const { createdInteraction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const createdInteraction = await trx
|
const createdInteraction = await trx
|
||||||
@@ -63,7 +62,7 @@ export class FileMarkOpenedMutationHandler
|
|||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
file_id: input.fileId,
|
file_id: input.fileId,
|
||||||
collaborator_id: input.userId,
|
collaborator_id: workspace.userId,
|
||||||
last_opened_at: lastOpenedAt,
|
last_opened_at: lastOpenedAt,
|
||||||
first_opened_at: firstOpenedAt,
|
first_opened_at: firstOpenedAt,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
@@ -87,7 +86,7 @@ export class FileMarkOpenedMutationHandler
|
|||||||
type: 'mark_file_opened',
|
type: 'mark_file_opened',
|
||||||
data: {
|
data: {
|
||||||
fileId: input.fileId,
|
fileId: input.fileId,
|
||||||
collaboratorId: input.userId,
|
collaboratorId: workspace.userId,
|
||||||
openedAt: new Date().toISOString(),
|
openedAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -114,14 +113,12 @@ export class FileMarkOpenedMutationHandler
|
|||||||
throw new Error('Failed to create file interaction');
|
throw new Error('Failed to create file interaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_interaction_updated',
|
type: 'file_interaction_updated',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
fileInteraction: mapFileInteraction(createdInteraction),
|
fileInteraction: mapFileInteraction(createdInteraction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { MarkFileSeenMutation, generateId, IdType } from '@colanode/core';
|
import { MarkFileSeenMutation, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FileMarkSeenMutationInput,
|
FileMarkSeenMutationInput,
|
||||||
@@ -8,18 +7,18 @@ import {
|
|||||||
} from '@/shared/mutations/files/file-mark-seen';
|
} from '@/shared/mutations/files/file-mark-seen';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { mapFileInteraction } from '@/main/utils';
|
import { mapFileInteraction } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FileMarkSeenMutationHandler
|
export class FileMarkSeenMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FileMarkSeenMutationInput>
|
implements MutationHandler<FileMarkSeenMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FileMarkSeenMutationInput
|
input: FileMarkSeenMutationInput
|
||||||
): Promise<FileMarkSeenMutationOutput> {
|
): Promise<FileMarkSeenMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const file = await workspaceDatabase
|
const file = await workspace.database
|
||||||
.selectFrom('files')
|
.selectFrom('files')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.fileId)
|
.where('id', '=', input.fileId)
|
||||||
@@ -31,11 +30,11 @@ export class FileMarkSeenMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingInteraction = await workspaceDatabase
|
const existingInteraction = await workspace.database
|
||||||
.selectFrom('file_interactions')
|
.selectFrom('file_interactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('file_id', '=', input.fileId)
|
.where('file_id', '=', input.fileId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (existingInteraction) {
|
if (existingInteraction) {
|
||||||
@@ -55,7 +54,7 @@ export class FileMarkSeenMutationHandler
|
|||||||
? existingInteraction.first_seen_at
|
? existingInteraction.first_seen_at
|
||||||
: lastSeenAt;
|
: lastSeenAt;
|
||||||
|
|
||||||
const { createdInteraction, createdMutation } = await workspaceDatabase
|
const { createdInteraction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const createdInteraction = await trx
|
const createdInteraction = await trx
|
||||||
@@ -63,7 +62,7 @@ export class FileMarkSeenMutationHandler
|
|||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
file_id: input.fileId,
|
file_id: input.fileId,
|
||||||
collaborator_id: input.userId,
|
collaborator_id: workspace.userId,
|
||||||
last_seen_at: lastSeenAt,
|
last_seen_at: lastSeenAt,
|
||||||
first_seen_at: firstSeenAt,
|
first_seen_at: firstSeenAt,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
@@ -87,7 +86,7 @@ export class FileMarkSeenMutationHandler
|
|||||||
type: 'mark_file_seen',
|
type: 'mark_file_seen',
|
||||||
data: {
|
data: {
|
||||||
fileId: input.fileId,
|
fileId: input.fileId,
|
||||||
collaboratorId: input.userId,
|
collaboratorId: workspace.userId,
|
||||||
seenAt: new Date().toISOString(),
|
seenAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -114,14 +113,12 @@ export class FileMarkSeenMutationHandler
|
|||||||
throw new Error('Failed to create file interaction');
|
throw new Error('Failed to create file interaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_interaction_updated',
|
type: 'file_interaction_updated',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
fileInteraction: mapFileInteraction(createdInteraction),
|
fileInteraction: mapFileInteraction(createdInteraction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,19 @@ import {
|
|||||||
FileSaveTempMutationOutput,
|
FileSaveTempMutationOutput,
|
||||||
} from '@/shared/mutations/files/file-save-temp';
|
} from '@/shared/mutations/files/file-save-temp';
|
||||||
import { getWorkspaceTempFilesDirectoryPath } from '@/main/utils';
|
import { getWorkspaceTempFilesDirectoryPath } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
export class FileSaveTempMutationHandler
|
export class FileSaveTempMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FileSaveTempMutationInput>
|
implements MutationHandler<FileSaveTempMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FileSaveTempMutationInput
|
input: FileSaveTempMutationInput
|
||||||
): Promise<FileSaveTempMutationOutput> {
|
): Promise<FileSaveTempMutationOutput> {
|
||||||
const directoryPath = getWorkspaceTempFilesDirectoryPath(input.userId);
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
const directoryPath = getWorkspaceTempFilesDirectoryPath(
|
||||||
|
workspace.accountId,
|
||||||
|
workspace.id
|
||||||
|
);
|
||||||
|
|
||||||
const fileName = this.generateUniqueName(directoryPath, input.name);
|
const fileName = this.generateUniqueName(directoryPath, input.name);
|
||||||
const filePath = path.join(directoryPath, fileName);
|
const filePath = path.join(directoryPath, fileName);
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import { FolderAttributes, generateId, IdType } from '@colanode/core';
|
import { FolderAttributes, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FolderCreateMutationInput,
|
FolderCreateMutationInput,
|
||||||
FolderCreateMutationOutput,
|
FolderCreateMutationOutput,
|
||||||
} from '@/shared/mutations/folders/folder-create';
|
} from '@/shared/mutations/folders/folder-create';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FolderCreateMutationHandler
|
export class FolderCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FolderCreateMutationInput>
|
implements MutationHandler<FolderCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FolderCreateMutationInput
|
input: FolderCreateMutationInput
|
||||||
): Promise<FolderCreateMutationOutput> {
|
): Promise<FolderCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
const id = generateId(IdType.Folder);
|
const id = generateId(IdType.Folder);
|
||||||
const attributes: FolderAttributes = {
|
const attributes: FolderAttributes = {
|
||||||
type: 'folder',
|
type: 'folder',
|
||||||
@@ -21,7 +24,7 @@ export class FolderCreateMutationHandler
|
|||||||
avatar: input.avatar,
|
avatar: input.avatar,
|
||||||
};
|
};
|
||||||
|
|
||||||
await entryService.createEntry(input.userId, {
|
await workspace.entries.createEntry({
|
||||||
id,
|
id,
|
||||||
attributes,
|
attributes,
|
||||||
parentId: input.parentId,
|
parentId: input.parentId,
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
FolderDeleteMutationInput,
|
FolderDeleteMutationInput,
|
||||||
FolderDeleteMutationOutput,
|
FolderDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/folders/folder-delete';
|
} from '@/shared/mutations/folders/folder-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FolderDeleteMutationHandler
|
export class FolderDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FolderDeleteMutationInput>
|
implements MutationHandler<FolderDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FolderDeleteMutationInput
|
input: FolderDeleteMutationInput
|
||||||
): Promise<FolderDeleteMutationOutput> {
|
): Promise<FolderDeleteMutationOutput> {
|
||||||
await entryService.deleteEntry(input.folderId, input.userId);
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
await workspace.entries.deleteEntry(input.folderId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { FolderAttributes } from '@colanode/core';
|
import { FolderAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
FolderUpdateMutationInput,
|
FolderUpdateMutationInput,
|
||||||
FolderUpdateMutationOutput,
|
FolderUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/folders/folder-update';
|
} from '@/shared/mutations/folders/folder-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class FolderUpdateMutationHandler
|
export class FolderUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<FolderUpdateMutationInput>
|
implements MutationHandler<FolderUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: FolderUpdateMutationInput
|
input: FolderUpdateMutationInput
|
||||||
): Promise<FolderUpdateMutationOutput> {
|
): Promise<FolderUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<FolderAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<FolderAttributes>(
|
||||||
input.folderId,
|
input.folderId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
attributes.name = input.name;
|
attributes.name = input.name;
|
||||||
attributes.avatar = input.avatar;
|
attributes.avatar = input.avatar;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
MessageType,
|
MessageType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { fileService } from '@/main/services/file-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { mapContentsToBlocks } from '@/shared/lib/editor';
|
import { mapContentsToBlocks } from '@/shared/lib/editor';
|
||||||
import {
|
import {
|
||||||
@@ -23,13 +22,13 @@ import {
|
|||||||
CreateFile,
|
CreateFile,
|
||||||
CreateFileState,
|
CreateFileState,
|
||||||
CreateMutation,
|
CreateMutation,
|
||||||
} from '@/main/data/workspace/schema';
|
} from '@/main/databases/workspace';
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import {
|
import {
|
||||||
fetchEntry,
|
fetchEntry,
|
||||||
fetchUser,
|
fetchUser,
|
||||||
fetchUserStorageUsed,
|
fetchUserStorageUsed,
|
||||||
|
getFileMetadata,
|
||||||
mapEntry,
|
mapEntry,
|
||||||
mapFile,
|
mapFile,
|
||||||
mapFileState,
|
mapFileState,
|
||||||
@@ -37,18 +36,18 @@ import {
|
|||||||
} from '@/main/utils';
|
} from '@/main/utils';
|
||||||
import { formatBytes } from '@/shared/lib/files';
|
import { formatBytes } from '@/shared/lib/files';
|
||||||
import { DownloadStatus, UploadStatus } from '@/shared/types/files';
|
import { DownloadStatus, UploadStatus } from '@/shared/types/files';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class MessageCreateMutationHandler
|
export class MessageCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<MessageCreateMutationInput>
|
implements MutationHandler<MessageCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: MessageCreateMutationInput
|
input: MessageCreateMutationInput
|
||||||
): Promise<MessageCreateMutationOutput> {
|
): Promise<MessageCreateMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const user = await fetchUser(workspaceDatabase, input.userId);
|
const user = await fetchUser(workspace.database, workspace.userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.UserNotFound,
|
MutationErrorCode.UserNotFound,
|
||||||
@@ -56,7 +55,7 @@ export class MessageCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await fetchEntry(workspaceDatabase, input.conversationId);
|
const entry = await fetchEntry(workspace.database, input.conversationId);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.EntryNotFound,
|
MutationErrorCode.EntryNotFound,
|
||||||
@@ -64,7 +63,7 @@ export class MessageCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await fetchEntry(workspaceDatabase, input.rootId);
|
const root = await fetchEntry(workspace.database, input.rootId);
|
||||||
if (!root) {
|
if (!root) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.RootNotFound,
|
MutationErrorCode.RootNotFound,
|
||||||
@@ -75,7 +74,7 @@ export class MessageCreateMutationHandler
|
|||||||
if (
|
if (
|
||||||
!canCreateMessage({
|
!canCreateMessage({
|
||||||
user: {
|
user: {
|
||||||
userId: input.userId,
|
userId: workspace.userId,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
},
|
},
|
||||||
root: mapEntry(root),
|
root: mapEntry(root),
|
||||||
@@ -101,7 +100,7 @@ export class MessageCreateMutationHandler
|
|||||||
for (const block of blocks) {
|
for (const block of blocks) {
|
||||||
if (block.type === EditorNodeTypes.FilePlaceholder) {
|
if (block.type === EditorNodeTypes.FilePlaceholder) {
|
||||||
const path = block.attrs?.path;
|
const path = block.attrs?.path;
|
||||||
const metadata = fileService.getFileMetadata(path);
|
const metadata = getFileMetadata(path);
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.FileInvalid,
|
MutationErrorCode.FileInvalid,
|
||||||
@@ -122,12 +121,7 @@ export class MessageCreateMutationHandler
|
|||||||
block.type = 'file';
|
block.type = 'file';
|
||||||
block.attrs = null;
|
block.attrs = null;
|
||||||
|
|
||||||
fileService.copyFileToWorkspace(
|
workspace.files.copyFileToWorkspace(path, fileId, metadata.extension);
|
||||||
path,
|
|
||||||
fileId,
|
|
||||||
metadata.extension,
|
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
files.push({
|
files.push({
|
||||||
id: fileId,
|
id: fileId,
|
||||||
@@ -142,7 +136,7 @@ export class MessageCreateMutationHandler
|
|||||||
extension: metadata.extension,
|
extension: metadata.extension,
|
||||||
size: metadata.size,
|
size: metadata.size,
|
||||||
created_at: createdAt,
|
created_at: createdAt,
|
||||||
created_by: input.userId,
|
created_by: workspace.userId,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -183,8 +177,8 @@ export class MessageCreateMutationHandler
|
|||||||
|
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
const storageUsed = await fetchUserStorageUsed(
|
const storageUsed = await fetchUserStorageUsed(
|
||||||
workspaceDatabase,
|
workspace.database,
|
||||||
input.userId
|
workspace.userId
|
||||||
);
|
);
|
||||||
|
|
||||||
const fileSizeSum = BigInt(
|
const fileSizeSum = BigInt(
|
||||||
@@ -193,7 +187,7 @@ export class MessageCreateMutationHandler
|
|||||||
|
|
||||||
if (storageUsed + fileSizeSum > user.storage_limit) {
|
if (storageUsed + fileSizeSum > user.storage_limit) {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
fileService.deleteFile(input.userId, file.id, file.extension);
|
workspace.files.deleteFile(file.id, file.extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
@@ -223,7 +217,7 @@ export class MessageCreateMutationHandler
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { createdMessage, createdFiles, createdFileStates } =
|
const { createdMessage, createdFiles, createdFileStates } =
|
||||||
await workspaceDatabase.transaction().execute(async (tx) => {
|
await workspace.database.transaction().execute(async (tx) => {
|
||||||
const createdMessage = await tx
|
const createdMessage = await tx
|
||||||
.insertInto('messages')
|
.insertInto('messages')
|
||||||
.returningAll()
|
.returningAll()
|
||||||
@@ -234,7 +228,7 @@ export class MessageCreateMutationHandler
|
|||||||
root_id: input.rootId,
|
root_id: input.rootId,
|
||||||
attributes: JSON.stringify(messageAttributes),
|
attributes: JSON.stringify(messageAttributes),
|
||||||
created_at: createdAt,
|
created_at: createdAt,
|
||||||
created_by: input.userId,
|
created_by: workspace.userId,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
})
|
})
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
@@ -301,7 +295,8 @@ export class MessageCreateMutationHandler
|
|||||||
if (createdMessage) {
|
if (createdMessage) {
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'message_created',
|
type: 'message_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
message: mapMessage(createdMessage),
|
message: mapMessage(createdMessage),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -310,7 +305,8 @@ export class MessageCreateMutationHandler
|
|||||||
for (const file of createdFiles) {
|
for (const file of createdFiles) {
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_created',
|
type: 'file_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
file: mapFile(file),
|
file: mapFile(file),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -320,16 +316,14 @@ export class MessageCreateMutationHandler
|
|||||||
for (const fileState of createdFileStates) {
|
for (const fileState of createdFileStates) {
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'file_state_created',
|
type: 'file_state_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
fileState: mapFileState(fileState),
|
fileState: mapFileState(fileState),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: messageId,
|
id: messageId,
|
||||||
|
|||||||
@@ -5,27 +5,25 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
MessageDeleteMutationInput,
|
MessageDeleteMutationInput,
|
||||||
MessageDeleteMutationOutput,
|
MessageDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/messages/message-delete';
|
} from '@/shared/mutations/messages/message-delete';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { fetchEntry, fetchUser, mapEntry, mapMessage } from '@/main/utils';
|
import { fetchEntry, mapEntry, mapMessage } from '@/main/utils';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
export class MessageDeleteMutationHandler
|
export class MessageDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<MessageDeleteMutationInput>
|
implements MutationHandler<MessageDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: MessageDeleteMutationInput
|
input: MessageDeleteMutationInput
|
||||||
): Promise<MessageDeleteMutationOutput> {
|
): Promise<MessageDeleteMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const message = await workspaceDatabase
|
const message = await workspace.database
|
||||||
.selectFrom('messages')
|
.selectFrom('messages')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.messageId)
|
.where('id', '=', input.messageId)
|
||||||
@@ -37,15 +35,7 @@ export class MessageDeleteMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await fetchUser(workspaceDatabase, input.userId);
|
const entry = await fetchEntry(workspace.database, message.entry_id);
|
||||||
if (!user) {
|
|
||||||
throw new MutationError(
|
|
||||||
MutationErrorCode.UserNotFound,
|
|
||||||
'There was an error while fetching the user. Please make sure you are logged in.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = await fetchEntry(workspaceDatabase, message.entry_id);
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.EntryNotFound,
|
MutationErrorCode.EntryNotFound,
|
||||||
@@ -53,7 +43,7 @@ export class MessageDeleteMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await fetchEntry(workspaceDatabase, message.root_id);
|
const root = await fetchEntry(workspace.database, message.root_id);
|
||||||
if (!root) {
|
if (!root) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.RootNotFound,
|
MutationErrorCode.RootNotFound,
|
||||||
@@ -64,8 +54,8 @@ export class MessageDeleteMutationHandler
|
|||||||
if (
|
if (
|
||||||
!canDeleteMessage({
|
!canDeleteMessage({
|
||||||
user: {
|
user: {
|
||||||
userId: input.userId,
|
userId: workspace.userId,
|
||||||
role: user.role,
|
role: workspace.role,
|
||||||
},
|
},
|
||||||
root: mapEntry(root),
|
root: mapEntry(root),
|
||||||
entry: mapEntry(entry),
|
entry: mapEntry(entry),
|
||||||
@@ -88,7 +78,7 @@ export class MessageDeleteMutationHandler
|
|||||||
deletedAt,
|
deletedAt,
|
||||||
};
|
};
|
||||||
|
|
||||||
await workspaceDatabase.transaction().execute(async (tx) => {
|
await workspace.database.transaction().execute(async (tx) => {
|
||||||
await tx
|
await tx
|
||||||
.updateTable('messages')
|
.updateTable('messages')
|
||||||
.set({
|
.set({
|
||||||
@@ -111,14 +101,12 @@ export class MessageDeleteMutationHandler
|
|||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'message_deleted',
|
type: 'message_deleted',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
message: mapMessage(message),
|
message: mapMessage(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { MarkMessageSeenMutation, generateId, IdType } from '@colanode/core';
|
import { MarkMessageSeenMutation, generateId, IdType } from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
MessageMarkSeenMutationInput,
|
MessageMarkSeenMutationInput,
|
||||||
@@ -8,18 +7,18 @@ import {
|
|||||||
} from '@/shared/mutations/messages/message-mark-seen';
|
} from '@/shared/mutations/messages/message-mark-seen';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import { mapMessageInteraction } from '@/main/utils';
|
import { mapMessageInteraction } from '@/main/utils';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class MessageMarkSeenMutationHandler
|
export class MessageMarkSeenMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<MessageMarkSeenMutationInput>
|
implements MutationHandler<MessageMarkSeenMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: MessageMarkSeenMutationInput
|
input: MessageMarkSeenMutationInput
|
||||||
): Promise<MessageMarkSeenMutationOutput> {
|
): Promise<MessageMarkSeenMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const message = await workspaceDatabase
|
const message = await workspace.database
|
||||||
.selectFrom('messages')
|
.selectFrom('messages')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.messageId)
|
.where('id', '=', input.messageId)
|
||||||
@@ -31,11 +30,11 @@ export class MessageMarkSeenMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingInteraction = await workspaceDatabase
|
const existingInteraction = await workspace.database
|
||||||
.selectFrom('message_interactions')
|
.selectFrom('message_interactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('message_id', '=', input.messageId)
|
.where('message_id', '=', input.messageId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (existingInteraction) {
|
if (existingInteraction) {
|
||||||
@@ -55,7 +54,7 @@ export class MessageMarkSeenMutationHandler
|
|||||||
? existingInteraction.first_seen_at
|
? existingInteraction.first_seen_at
|
||||||
: lastSeenAt;
|
: lastSeenAt;
|
||||||
|
|
||||||
const { createdInteraction, createdMutation } = await workspaceDatabase
|
const { createdInteraction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const createdInteraction = await trx
|
const createdInteraction = await trx
|
||||||
@@ -63,7 +62,7 @@ export class MessageMarkSeenMutationHandler
|
|||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
message_id: input.messageId,
|
message_id: input.messageId,
|
||||||
collaborator_id: input.userId,
|
collaborator_id: workspace.userId,
|
||||||
first_seen_at: firstSeenAt,
|
first_seen_at: firstSeenAt,
|
||||||
last_seen_at: lastSeenAt,
|
last_seen_at: lastSeenAt,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
@@ -87,7 +86,7 @@ export class MessageMarkSeenMutationHandler
|
|||||||
type: 'mark_message_seen',
|
type: 'mark_message_seen',
|
||||||
data: {
|
data: {
|
||||||
messageId: input.messageId,
|
messageId: input.messageId,
|
||||||
collaboratorId: input.userId,
|
collaboratorId: workspace.userId,
|
||||||
seenAt: new Date().toISOString(),
|
seenAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -114,14 +113,12 @@ export class MessageMarkSeenMutationHandler
|
|||||||
throw new Error('Failed to create message interaction');
|
throw new Error('Failed to create message interaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'message_interaction_updated',
|
type: 'message_interaction_updated',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
messageInteraction: mapMessageInteraction(createdInteraction),
|
messageInteraction: mapMessageInteraction(createdInteraction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,32 +5,26 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
MessageReactionCreateMutationInput,
|
MessageReactionCreateMutationInput,
|
||||||
MessageReactionCreateMutationOutput,
|
MessageReactionCreateMutationOutput,
|
||||||
} from '@/shared/mutations/messages/message-reaction-create';
|
} from '@/shared/mutations/messages/message-reaction-create';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
import {
|
import { fetchEntry, mapEntry, mapMessageReaction } from '@/main/utils';
|
||||||
fetchEntry,
|
|
||||||
fetchUser,
|
|
||||||
mapEntry,
|
|
||||||
mapMessageReaction,
|
|
||||||
} from '@/main/utils';
|
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class MessageReactionCreateMutationHandler
|
export class MessageReactionCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<MessageReactionCreateMutationInput>
|
implements MutationHandler<MessageReactionCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: MessageReactionCreateMutationInput
|
input: MessageReactionCreateMutationInput
|
||||||
): Promise<MessageReactionCreateMutationOutput> {
|
): Promise<MessageReactionCreateMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const message = await workspaceDatabase
|
const message = await workspace.database
|
||||||
.selectFrom('messages')
|
.selectFrom('messages')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', input.messageId)
|
.where('id', '=', input.messageId)
|
||||||
@@ -43,11 +37,11 @@ export class MessageReactionCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingMessageReaction = await workspaceDatabase
|
const existingMessageReaction = await workspace.database
|
||||||
.selectFrom('message_reactions')
|
.selectFrom('message_reactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('message_id', '=', input.messageId)
|
.where('message_id', '=', input.messageId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.where('reaction', '=', input.reaction)
|
.where('reaction', '=', input.reaction)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
@@ -57,15 +51,7 @@ export class MessageReactionCreateMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await fetchUser(workspaceDatabase, input.userId);
|
const root = await fetchEntry(workspace.database, input.rootId);
|
||||||
if (!user) {
|
|
||||||
throw new MutationError(
|
|
||||||
MutationErrorCode.UserNotFound,
|
|
||||||
'There was an error while fetching the user. Please make sure you are logged in.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = await fetchEntry(workspaceDatabase, input.rootId);
|
|
||||||
if (!root) {
|
if (!root) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.RootNotFound,
|
MutationErrorCode.RootNotFound,
|
||||||
@@ -76,8 +62,8 @@ export class MessageReactionCreateMutationHandler
|
|||||||
if (
|
if (
|
||||||
!canCreateMessageReaction({
|
!canCreateMessageReaction({
|
||||||
user: {
|
user: {
|
||||||
userId: input.userId,
|
userId: workspace.userId,
|
||||||
role: user.role,
|
role: workspace.role,
|
||||||
},
|
},
|
||||||
root: mapEntry(root),
|
root: mapEntry(root),
|
||||||
message: {
|
message: {
|
||||||
@@ -92,7 +78,7 @@ export class MessageReactionCreateMutationHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { createdMessageReaction, createdMutation } = await workspaceDatabase
|
const { createdMessageReaction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const createdMessageReaction = await trx
|
const createdMessageReaction = await trx
|
||||||
@@ -100,7 +86,7 @@ export class MessageReactionCreateMutationHandler
|
|||||||
.returningAll()
|
.returningAll()
|
||||||
.values({
|
.values({
|
||||||
message_id: input.messageId,
|
message_id: input.messageId,
|
||||||
collaborator_id: input.userId,
|
collaborator_id: workspace.userId,
|
||||||
reaction: input.reaction,
|
reaction: input.reaction,
|
||||||
root_id: input.rootId,
|
root_id: input.rootId,
|
||||||
version: 0n,
|
version: 0n,
|
||||||
@@ -153,14 +139,12 @@ export class MessageReactionCreateMutationHandler
|
|||||||
throw new Error('Failed to create message reaction');
|
throw new Error('Failed to create message reaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'message_reaction_created',
|
type: 'message_reaction_created',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
messageReaction: mapMessageReaction(createdMessageReaction),
|
messageReaction: mapMessageReaction(createdMessageReaction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
IdType,
|
IdType,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
|
||||||
import { databaseService } from '@/main/data/database-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
MessageReactionDeleteMutationInput,
|
MessageReactionDeleteMutationInput,
|
||||||
@@ -12,22 +11,22 @@ import {
|
|||||||
} from '@/shared/mutations/messages/message-reaction-delete';
|
} from '@/shared/mutations/messages/message-reaction-delete';
|
||||||
import { mapMessageReaction } from '@/main/utils';
|
import { mapMessageReaction } from '@/main/utils';
|
||||||
import { eventBus } from '@/shared/lib/event-bus';
|
import { eventBus } from '@/shared/lib/event-bus';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class MessageReactionDeleteMutationHandler
|
export class MessageReactionDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<MessageReactionDeleteMutationInput>
|
implements MutationHandler<MessageReactionDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: MessageReactionDeleteMutationInput
|
input: MessageReactionDeleteMutationInput
|
||||||
): Promise<MessageReactionDeleteMutationOutput> {
|
): Promise<MessageReactionDeleteMutationOutput> {
|
||||||
const workspaceDatabase = await databaseService.getWorkspaceDatabase(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
input.userId
|
|
||||||
);
|
|
||||||
|
|
||||||
const existingMessageReaction = await workspaceDatabase
|
const existingMessageReaction = await workspace.database
|
||||||
.selectFrom('message_reactions')
|
.selectFrom('message_reactions')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('message_id', '=', input.messageId)
|
.where('message_id', '=', input.messageId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.where('reaction', '=', input.reaction)
|
.where('reaction', '=', input.reaction)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ export class MessageReactionDeleteMutationHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const { deletedMessageReaction, createdMutation } = await workspaceDatabase
|
const { deletedMessageReaction, createdMutation } = await workspace.database
|
||||||
.transaction()
|
.transaction()
|
||||||
.execute(async (trx) => {
|
.execute(async (trx) => {
|
||||||
const deletedMessageReaction = await trx
|
const deletedMessageReaction = await trx
|
||||||
@@ -47,7 +46,7 @@ export class MessageReactionDeleteMutationHandler
|
|||||||
deleted_at: new Date().toISOString(),
|
deleted_at: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
.where('message_id', '=', input.messageId)
|
.where('message_id', '=', input.messageId)
|
||||||
.where('collaborator_id', '=', input.userId)
|
.where('collaborator_id', '=', workspace.userId)
|
||||||
.where('reaction', '=', input.reaction)
|
.where('reaction', '=', input.reaction)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|
||||||
@@ -89,14 +88,12 @@ export class MessageReactionDeleteMutationHandler
|
|||||||
throw new Error('Failed to delete message reaction');
|
throw new Error('Failed to delete message reaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.publish({
|
workspace.mutations.triggerSync();
|
||||||
type: 'mutation_created',
|
|
||||||
userId: input.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.publish({
|
eventBus.publish({
|
||||||
type: 'message_reaction_deleted',
|
type: 'message_reaction_deleted',
|
||||||
userId: input.userId,
|
accountId: workspace.accountId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
messageReaction: mapMessageReaction(deletedMessageReaction),
|
messageReaction: mapMessageReaction(deletedMessageReaction),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { PageAttributes } from '@colanode/core';
|
import { PageAttributes } from '@colanode/core';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { mapContentsToBlocks } from '@/shared/lib/editor';
|
import { mapContentsToBlocks } from '@/shared/lib/editor';
|
||||||
import {
|
import {
|
||||||
@@ -9,16 +8,19 @@ import {
|
|||||||
PageContentUpdateMutationOutput,
|
PageContentUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/pages/page-content-update';
|
} from '@/shared/mutations/pages/page-content-update';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class PageContentUpdateMutationHandler
|
export class PageContentUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<PageContentUpdateMutationInput>
|
implements MutationHandler<PageContentUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: PageContentUpdateMutationInput
|
input: PageContentUpdateMutationInput
|
||||||
): Promise<PageContentUpdateMutationOutput> {
|
): Promise<PageContentUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<PageAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<PageAttributes>(
|
||||||
input.pageId,
|
input.pageId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const indexMap = new Map<string, string>();
|
const indexMap = new Map<string, string>();
|
||||||
if (attributes.content) {
|
if (attributes.content) {
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import { generateId, IdType, PageAttributes } from '@colanode/core';
|
import { generateId, IdType, PageAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
PageCreateMutationInput,
|
PageCreateMutationInput,
|
||||||
PageCreateMutationOutput,
|
PageCreateMutationOutput,
|
||||||
} from '@/shared/mutations/pages/page-create';
|
} from '@/shared/mutations/pages/page-create';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class PageCreateMutationHandler
|
export class PageCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<PageCreateMutationInput>
|
implements MutationHandler<PageCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: PageCreateMutationInput
|
input: PageCreateMutationInput
|
||||||
): Promise<PageCreateMutationOutput> {
|
): Promise<PageCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
const id = generateId(IdType.Page);
|
const id = generateId(IdType.Page);
|
||||||
const attributes: PageAttributes = {
|
const attributes: PageAttributes = {
|
||||||
type: 'page',
|
type: 'page',
|
||||||
@@ -21,7 +24,7 @@ export class PageCreateMutationHandler
|
|||||||
name: input.name,
|
name: input.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
await entryService.createEntry(input.userId, {
|
await workspace.entries.createEntry({
|
||||||
id,
|
id,
|
||||||
attributes,
|
attributes,
|
||||||
parentId: input.parentId,
|
parentId: input.parentId,
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
|
||||||
import {
|
import {
|
||||||
PageDeleteMutationInput,
|
PageDeleteMutationInput,
|
||||||
PageDeleteMutationOutput,
|
PageDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/pages/page-delete';
|
} from '@/shared/mutations/pages/page-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
import { MutationHandler } from '@/main/types';
|
||||||
|
|
||||||
export class PageDeleteMutationHandler
|
export class PageDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<PageDeleteMutationInput>
|
implements MutationHandler<PageDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: PageDeleteMutationInput
|
input: PageDeleteMutationInput
|
||||||
): Promise<PageDeleteMutationOutput> {
|
): Promise<PageDeleteMutationOutput> {
|
||||||
await entryService.deleteEntry(input.pageId, input.userId);
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
await workspace.entries.deleteEntry(input.pageId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { PageAttributes } from '@colanode/core';
|
import { PageAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
PageUpdateMutationInput,
|
PageUpdateMutationInput,
|
||||||
PageUpdateMutationOutput,
|
PageUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/pages/page-update';
|
} from '@/shared/mutations/pages/page-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class PageUpdateMutationHandler
|
export class PageUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<PageUpdateMutationInput>
|
implements MutationHandler<PageUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: PageUpdateMutationInput
|
input: PageUpdateMutationInput
|
||||||
): Promise<PageUpdateMutationOutput> {
|
): Promise<PageUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<PageAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<PageAttributes>(
|
||||||
input.pageId,
|
input.pageId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
attributes.name = input.name;
|
attributes.name = input.name;
|
||||||
attributes.avatar = input.avatar;
|
attributes.avatar = input.avatar;
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { RecordAttributes } from '@colanode/core';
|
import { RecordAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
RecordAvatarUpdateMutationInput,
|
RecordAvatarUpdateMutationInput,
|
||||||
RecordAvatarUpdateMutationOutput,
|
RecordAvatarUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/records/record-avatar-update';
|
} from '@/shared/mutations/records/record-avatar-update';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class RecordAvatarUpdateMutationHandler
|
export class RecordAvatarUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<RecordAvatarUpdateMutationInput>
|
implements MutationHandler<RecordAvatarUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: RecordAvatarUpdateMutationInput
|
input: RecordAvatarUpdateMutationInput
|
||||||
): Promise<RecordAvatarUpdateMutationOutput> {
|
): Promise<RecordAvatarUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<RecordAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<RecordAttributes>(
|
||||||
input.recordId,
|
input.recordId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
attributes.avatar = input.avatar;
|
attributes.avatar = input.avatar;
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { RecordAttributes } from '@colanode/core';
|
import { RecordAttributes } from '@colanode/core';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { mapContentsToBlocks } from '@/shared/lib/editor';
|
import { mapContentsToBlocks } from '@/shared/lib/editor';
|
||||||
import {
|
import {
|
||||||
@@ -9,16 +8,19 @@ import {
|
|||||||
RecordContentUpdateMutationOutput,
|
RecordContentUpdateMutationOutput,
|
||||||
} from '@/shared/mutations/records/record-content-update';
|
} from '@/shared/mutations/records/record-content-update';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class RecordContentUpdateMutationHandler
|
export class RecordContentUpdateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<RecordContentUpdateMutationInput>
|
implements MutationHandler<RecordContentUpdateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: RecordContentUpdateMutationInput
|
input: RecordContentUpdateMutationInput
|
||||||
): Promise<RecordContentUpdateMutationOutput> {
|
): Promise<RecordContentUpdateMutationOutput> {
|
||||||
const result = await entryService.updateEntry<RecordAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<RecordAttributes>(
|
||||||
input.recordId,
|
input.recordId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
const indexMap = new Map<string, string>();
|
const indexMap = new Map<string, string>();
|
||||||
if (attributes.content) {
|
if (attributes.content) {
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import { generateId, IdType, RecordAttributes } from '@colanode/core';
|
import { generateId, IdType, RecordAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
RecordCreateMutationInput,
|
RecordCreateMutationInput,
|
||||||
RecordCreateMutationOutput,
|
RecordCreateMutationOutput,
|
||||||
} from '@/shared/mutations/records/record-create';
|
} from '@/shared/mutations/records/record-create';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class RecordCreateMutationHandler
|
export class RecordCreateMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<RecordCreateMutationInput>
|
implements MutationHandler<RecordCreateMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: RecordCreateMutationInput
|
input: RecordCreateMutationInput
|
||||||
): Promise<RecordCreateMutationOutput> {
|
): Promise<RecordCreateMutationOutput> {
|
||||||
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
const id = generateId(IdType.Record);
|
const id = generateId(IdType.Record);
|
||||||
const attributes: RecordAttributes = {
|
const attributes: RecordAttributes = {
|
||||||
type: 'record',
|
type: 'record',
|
||||||
@@ -22,7 +25,7 @@ export class RecordCreateMutationHandler
|
|||||||
fields: input.fields ?? {},
|
fields: input.fields ?? {},
|
||||||
};
|
};
|
||||||
|
|
||||||
await entryService.createEntry(input.userId, {
|
await workspace.entries.createEntry({
|
||||||
id,
|
id,
|
||||||
attributes,
|
attributes,
|
||||||
parentId: input.databaseId,
|
parentId: input.databaseId,
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import {
|
import {
|
||||||
RecordDeleteMutationInput,
|
RecordDeleteMutationInput,
|
||||||
RecordDeleteMutationOutput,
|
RecordDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/records/record-delete';
|
} from '@/shared/mutations/records/record-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class RecordDeleteMutationHandler
|
export class RecordDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<RecordDeleteMutationInput>
|
implements MutationHandler<RecordDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: RecordDeleteMutationInput
|
input: RecordDeleteMutationInput
|
||||||
): Promise<RecordDeleteMutationOutput> {
|
): Promise<RecordDeleteMutationOutput> {
|
||||||
await entryService.deleteEntry(input.recordId, input.userId);
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
await workspace.entries.deleteEntry(input.recordId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { RecordAttributes } from '@colanode/core';
|
import { RecordAttributes } from '@colanode/core';
|
||||||
|
|
||||||
import { entryService } from '@/main/services/entry-service';
|
|
||||||
import { MutationHandler } from '@/main/types';
|
import { MutationHandler } from '@/main/types';
|
||||||
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
import { MutationError, MutationErrorCode } from '@/shared/mutations';
|
||||||
import {
|
import {
|
||||||
RecordFieldValueDeleteMutationInput,
|
RecordFieldValueDeleteMutationInput,
|
||||||
RecordFieldValueDeleteMutationOutput,
|
RecordFieldValueDeleteMutationOutput,
|
||||||
} from '@/shared/mutations/records/record-field-value-delete';
|
} from '@/shared/mutations/records/record-field-value-delete';
|
||||||
|
import { WorkspaceMutationHandlerBase } from '@/main/mutations/workspace-mutation-handler-base';
|
||||||
|
|
||||||
export class RecordFieldValueDeleteMutationHandler
|
export class RecordFieldValueDeleteMutationHandler
|
||||||
|
extends WorkspaceMutationHandlerBase
|
||||||
implements MutationHandler<RecordFieldValueDeleteMutationInput>
|
implements MutationHandler<RecordFieldValueDeleteMutationInput>
|
||||||
{
|
{
|
||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: RecordFieldValueDeleteMutationInput
|
input: RecordFieldValueDeleteMutationInput
|
||||||
): Promise<RecordFieldValueDeleteMutationOutput> {
|
): Promise<RecordFieldValueDeleteMutationOutput> {
|
||||||
const result = await entryService.updateEntry<RecordAttributes>(
|
const workspace = this.getWorkspace(input.accountId, input.workspaceId);
|
||||||
|
|
||||||
|
const result = await workspace.entries.updateEntry<RecordAttributes>(
|
||||||
input.recordId,
|
input.recordId,
|
||||||
input.userId,
|
|
||||||
(attributes) => {
|
(attributes) => {
|
||||||
delete attributes.fields[input.fieldId];
|
delete attributes.fields[input.fieldId];
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user