Improve account login/logout and some redirect workflows

This commit is contained in:
Hakan Shehu
2024-10-27 10:51:27 +01:00
parent 31220e946b
commit 8ffe8f6ce5
11 changed files with 107 additions and 57 deletions

View File

@@ -44,9 +44,11 @@ const createWindow = async () => {
mainWindow.webContents.openDevTools();
}
subscriptionId = eventBus.subscribe((event) => {
mainWindow.webContents.send('event', event);
});
if (subscriptionId === null) {
subscriptionId = eventBus.subscribe((event) => {
mainWindow.webContents.send('event', event);
});
}
protocol.handle('avatar', (request) => {
return avatarManager.handleAvatarRequest(request);
@@ -94,7 +96,7 @@ app.on('activate', () => {
});
app.on('before-quit', () => {
if (subscriptionId) {
if (subscriptionId !== null) {
eventBus.unsubscribe(subscriptionId);
}
});

View File

@@ -25,7 +25,7 @@ const createServersTable: Migration = {
created_at: new Date().toISOString(),
},
{
domain: 'eu.neuronapp.io',
domain: 'server-eu.neuronapp.io',
name: 'Neuron Cloud (EU)',
avatar: '',
attributes: '{}',
@@ -33,7 +33,7 @@ const createServersTable: Migration = {
created_at: new Date().toISOString(),
},
{
domain: 'us.neuronapp.io',
domain: 'server-us.neuronapp.io',
name: 'Neuron Cloud (US)',
avatar: '',
attributes: '{}',
@@ -88,8 +88,24 @@ const createWorkspacesTable: Migration = {
},
};
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();
},
};
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,
};

View File

@@ -44,8 +44,16 @@ export type SelectWorkspace = Selectable<WorkspaceTable>;
export type CreateWorkspace = Insertable<WorkspaceTable>;
export type UpdateWorkspace = Updateable<WorkspaceTable>;
interface DeletedTokenTable {
token: ColumnType<string, string, never>;
account_id: ColumnType<string, string, never>;
server: ColumnType<string, string, never>;
created_at: ColumnType<string, string, string>;
}
export interface AppDatabaseSchema {
servers: ServerTable;
accounts: AccountTable;
workspaces: WorkspaceTable;
deleted_tokens: DeletedTokenTable;
}

View File

@@ -1,6 +1,11 @@
import fs from 'fs';
import { databaseManager } from '@/main/data/database-manager';
import { LogoutMutationInput } from '@/operations/mutations/logout';
import { MutationHandler, MutationResult } from '@/operations/mutations';
import {
getAccountAvatarsDirectoryPath,
getWorkspaceDirectoryPath,
} from '@/main/utils';
export class LogoutMutationHandler
implements MutationHandler<LogoutMutationInput>
@@ -22,12 +27,44 @@ export class LogoutMutationHandler
};
}
const workspaces = await databaseManager.appDatabase
.selectFrom('workspaces')
.selectAll()
.where('account_id', '=', account.id)
.execute();
for (const workspace of workspaces) {
await databaseManager.deleteWorkspaceDatabase(workspace.user_id);
const workspaceDir = getWorkspaceDirectoryPath(workspace.user_id);
if (fs.existsSync(workspaceDir)) {
fs.rmSync(workspaceDir, { recursive: true });
}
}
const avatarsDir = getAccountAvatarsDirectoryPath(account.id);
if (fs.existsSync(avatarsDir)) {
fs.rmSync(avatarsDir, { recursive: true });
}
await databaseManager.appDatabase
.updateTable('accounts')
.set({
status: 'logged_out',
.deleteFrom('accounts')
.where('id', '=', account.id)
.execute();
await databaseManager.appDatabase
.deleteFrom('workspaces')
.where('account_id', '=', account.id)
.execute();
await databaseManager.appDatabase
.insertInto('deleted_tokens')
.values({
token: account.token,
account_id: account.id,
server: account.server,
created_at: new Date().toISOString(),
})
.where('id', '=', input.accountId)
.execute();
return {

View File

@@ -29,7 +29,7 @@ class Synchronizer {
private async executeEventLoop() {
try {
await this.syncLoggedOutAccounts();
await this.syncDeletedTokens();
await this.syncWorkspaces();
} catch (error) {
console.log('error', error);
@@ -38,63 +38,38 @@ class Synchronizer {
setTimeout(this.executeEventLoop, EVENT_LOOP_INTERVAL);
}
private async syncLoggedOutAccounts() {
const accounts = await databaseManager.appDatabase
.selectFrom('accounts')
.innerJoin('servers', 'accounts.server', 'servers.domain')
private async syncDeletedTokens() {
const deletedTokens = await databaseManager.appDatabase
.selectFrom('deleted_tokens')
.innerJoin('servers', 'deleted_tokens.server', 'servers.domain')
.select([
'accounts.id',
'accounts.token',
'deleted_tokens.token',
'deleted_tokens.account_id',
'servers.domain',
'servers.attributes',
])
.where('status', '=', 'logged_out')
.execute();
if (accounts.length === 0) {
if (deletedTokens.length === 0) {
return;
}
for (const account of accounts) {
for (const deletedToken of deletedTokens) {
try {
const { status } = await httpClient.delete(`/v1/accounts/logout`, {
serverDomain: account.domain,
serverAttributes: account.attributes,
token: account.token,
serverDomain: deletedToken.domain,
serverAttributes: deletedToken.attributes,
token: deletedToken.token,
});
if (status !== 200) {
return;
}
const workspaces = await databaseManager.appDatabase
.selectFrom('workspaces')
.selectAll()
.where('account_id', '=', account.id)
.execute();
for (const workspace of workspaces) {
await databaseManager.deleteWorkspaceDatabase(workspace.user_id);
const workspaceDir = getWorkspaceDirectoryPath(workspace.user_id);
if (fs.existsSync(workspaceDir)) {
fs.rmSync(workspaceDir, { recursive: true });
}
}
const avatarsDir = getAccountAvatarsDirectoryPath(account.id);
if (fs.existsSync(avatarsDir)) {
fs.rmSync(avatarsDir, { recursive: true });
}
await databaseManager.appDatabase
.deleteFrom('accounts')
.where('id', '=', account.id)
.execute();
await databaseManager.appDatabase
.deleteFrom('workspaces')
.where('account_id', '=', account.id)
.deleteFrom('deleted_tokens')
.where('token', '=', deletedToken.token)
.where('account_id', '=', deletedToken.account_id)
.execute();
} catch (error) {
// console.log('error', error);

View File

@@ -16,9 +16,14 @@ import { toast } from '@/renderer/hooks/use-toast';
interface AccountLogoutProps {
id: string;
onCancel: () => void;
onLogout: () => void;
}
export const AccountLogout = ({ id, onCancel }: AccountLogoutProps) => {
export const AccountLogout = ({
id,
onCancel,
onLogout,
}: AccountLogoutProps) => {
const { mutate, isPending } = useMutation();
return (
<AlertDialog
@@ -48,7 +53,7 @@ export const AccountLogout = ({ id, onCancel }: AccountLogoutProps) => {
accountId: id,
},
onSuccess() {
window.location.href = '/';
onLogout();
},
onError() {
toast({

View File

@@ -16,6 +16,7 @@ import { toast } from '@/renderer/hooks/use-toast';
import { Server } from '@/types/servers';
import { useMutation } from '@/renderer/hooks/use-mutation';
import { Mail } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
const formSchema = z.object({
email: z.string().min(2).email(),
@@ -27,6 +28,7 @@ interface EmailLoginProps {
}
export const EmailLogin = ({ server }: EmailLoginProps) => {
const navigate = useNavigate();
const { mutate, isPending } = useMutation();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
@@ -45,7 +47,7 @@ export const EmailLogin = ({ server }: EmailLoginProps) => {
server: server.domain,
},
onSuccess() {
window.location.href = '/';
navigate('/');
},
onError() {
toast({

View File

@@ -16,6 +16,7 @@ import { toast } from '@/renderer/hooks/use-toast';
import { useMutation } from '@/renderer/hooks/use-mutation';
import { Server } from '@/types/servers';
import { Mail } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
const formSchema = z.object({
name: z.string().min(2),
@@ -28,6 +29,7 @@ interface EmailRegisterProps {
}
export const EmailRegister = ({ server }: EmailRegisterProps) => {
const navigate = useNavigate();
const { mutate, isPending } = useMutation();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
@@ -48,7 +50,7 @@ export const EmailRegister = ({ server }: EmailRegisterProps) => {
server: server.domain,
},
onSuccess() {
window.location.href = '/';
navigate('/');
},
onError() {
toast({

View File

@@ -63,6 +63,9 @@ export const App = () => {
onCancel={() => {
setShowLogout(false);
}}
onLogout={() => {
setShowLogout(false);
}}
/>
)}
</AccountContext.Provider>

View File

@@ -12,7 +12,7 @@ export const ChatCreatePopover = () => {
return (
<Popover open={open} onOpenChange={setOpen} modal={true}>
<PopoverTrigger>
<PopoverTrigger asChild>
<Plus className="mr-2 size-4 cursor-pointer" />
</PopoverTrigger>
<PopoverContent className="w-96 p-1">

View File

@@ -32,8 +32,8 @@ export const WorkspaceCreate = () => {
accountId: account.id,
avatar: values.avatar,
},
onSuccess() {
window.location.href = '/';
onSuccess(id) {
navigate(`/${id}`);
},
onError() {
toast({