mirror of
https://github.com/colanode/colanode.git
synced 2025-12-29 00:25:03 +01:00
Improve account login/logout and some redirect workflows
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -63,6 +63,9 @@ export const App = () => {
|
||||
onCancel={() => {
|
||||
setShowLogout(false);
|
||||
}}
|
||||
onLogout={() => {
|
||||
setShowLogout(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AccountContext.Provider>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -32,8 +32,8 @@ export const WorkspaceCreate = () => {
|
||||
accountId: account.id,
|
||||
avatar: values.avatar,
|
||||
},
|
||||
onSuccess() {
|
||||
window.location.href = '/';
|
||||
onSuccess(id) {
|
||||
navigate(`/${id}`);
|
||||
},
|
||||
onError() {
|
||||
toast({
|
||||
|
||||
Reference in New Issue
Block a user