Migrate users/network/database/backups to new format

This commit is contained in:
Dane Everitt 2022-02-27 12:25:31 -05:00
parent e908c391ee
commit bfd92c314d
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
52 changed files with 391 additions and 352 deletions

View file

@ -0,0 +1,61 @@
import http, { getPaginationSet, PaginatedResult } from '@/api/http';
import { Transformers, Server } from '@definitions/user';
import { PanelPermissions } from '@/state/permissions';
interface QueryParams {
query?: string;
page?: number;
type?: string;
}
const getServers = ({ query, ...params }: QueryParams): Promise<PaginatedResult<Server>> => {
return new Promise((resolve, reject) => {
http.get('/api/client', {
params: {
'filter[*]': query,
...params,
},
})
.then(({ data }) => resolve({
items: (data.data || []).map(Transformers.toServer),
pagination: getPaginationSet(data.meta.pagination),
}))
.catch(reject);
});
};
const getSystemPermissions = (): Promise<PanelPermissions> => {
return new Promise((resolve, reject) => {
http.get('/api/client/permissions')
.then(({ data }) => resolve(data.attributes.permissions))
.catch(reject);
});
};
const updateAccountEmail = (email: string, password: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.put('/api/client/account/email', { email, password })
.then(() => resolve())
.catch(reject);
});
};
interface UpdateAccountPasswordData {
current: string;
password: string;
confirmPassword: string;
}
const updateAccountPassword = ({ current, password, confirmPassword }: UpdateAccountPasswordData): Promise<void> => {
return new Promise((resolve, reject) => {
http.put('/api/client/account/password', {
current_password: current,
password: password,
password_confirmation: confirmPassword,
})
.then(() => resolve())
.catch(reject);
});
};
export { getServers, getSystemPermissions, updateAccountEmail, updateAccountPassword };

View file

@ -1,9 +0,0 @@
import http from '@/api/http';
export default (email: string, password: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.put('/api/client/account/email', { email, password })
.then(() => resolve())
.catch(reject);
});
};

View file

@ -1,19 +0,0 @@
import http from '@/api/http';
interface Data {
current: string;
password: string;
confirmPassword: string;
}
export default ({ current, password, confirmPassword }: Data): Promise<void> => {
return new Promise((resolve, reject) => {
http.put('/api/client/account/password', {
current_password: current,
password: password,
password_confirmation: confirmPassword,
})
.then(() => resolve())
.catch(reject);
});
};

View file

@ -1,5 +1,6 @@
import { Model, UUID } from '@/api/definitions'; import { Model, UUID } from '@/api/definitions';
import { ServerEggVariable, ServerStatus } from '@/api/server/types';
export type ServerStatus = 'installing' | 'install_failed' | 'suspended' | 'restoring_backup' | null;
interface SecurityKey extends Model { interface SecurityKey extends Model {
uuid: UUID; uuid: UUID;
@ -60,3 +61,57 @@ interface Server extends Model {
variables: ServerEggVariable[]; variables: ServerEggVariable[];
allocations: Allocation[]; allocations: Allocation[];
} }
interface ServerBackup extends Model {
uuid: UUID;
isSuccessful: boolean;
isLocked: boolean;
name: string;
ignoredFiles: string;
checksum: string;
bytes: number;
createdAt: Date;
completedAt: Date | null;
}
interface ServerEggVariable extends Model {
name: string;
description: string;
envVariable: string;
defaultValue: string;
serverValue: string;
isEditable: boolean;
rules: string[];
}
interface ServerDatabase extends Model {
id: string;
name: string;
username: string;
connectionString: string;
allowConnectionsFrom: string;
password?: string;
}
export type SubuserPermission =
'websocket.connect' |
'control.console' | 'control.start' | 'control.stop' | 'control.restart' |
'user.create' | 'user.read' | 'user.update' | 'user.delete' |
'file.create' | 'file.read' | 'file.update' | 'file.delete' | 'file.archive' | 'file.sftp' |
'allocation.read' | 'allocation.update' |
'startup.read' | 'startup.update' |
'database.create' | 'database.read' | 'database.update' | 'database.delete' | 'database.view_password' |
'schedule.create' | 'schedule.read' | 'schedule.update' | 'schedule.delete'
;
interface Subuser extends Model {
uuid: string;
username: string;
email: string;
image: string;
twoFactorEnabled: boolean;
createdAt: Date;
permissions: SubuserPermission[];
can (permission: SubuserPermission): boolean;
}

View file

@ -1,6 +1,5 @@
import * as Models from './models'; import * as Models from './models';
import { FractalResponseData, FractalResponseList } from '@/api/http'; import { FractalResponseData, FractalResponseList } from '@/api/http';
import { rawDataToServerEggVariable } from '@/api/transformers';
export default class Transformers { export default class Transformers {
static toSecurityKey ({ attributes }: FractalResponseData): Models.SecurityKey { static toSecurityKey ({ attributes }: FractalResponseData): Models.SecurityKey {
@ -55,8 +54,59 @@ export default class Transformers {
featureLimits: { ...attributes.feature_limits }, featureLimits: { ...attributes.feature_limits },
isInstalling: attributes.status === 'installing' || attributes.status === 'install_failed', isInstalling: attributes.status === 'installing' || attributes.status === 'install_failed',
isTransferring: attributes.is_transferring, isTransferring: attributes.is_transferring,
variables: ((attributes.relationships?.variables as FractalResponseList | undefined)?.data || []).map(rawDataToServerEggVariable), variables: ((attributes.relationships?.variables as FractalResponseList | undefined)?.data || []).map(this.toServerEggVariable),
allocations: ((attributes.relationships?.allocations as FractalResponseList | undefined)?.data || []).map(this.toServerAllocation), allocations: ((attributes.relationships?.allocations as FractalResponseList | undefined)?.data || []).map(this.toServerAllocation),
}; };
} }
static toServerBackup ({ attributes }: FractalResponseData): Models.ServerBackup {
return {
uuid: attributes.uuid,
isSuccessful: attributes.is_successful,
isLocked: attributes.is_locked,
name: attributes.name,
ignoredFiles: attributes.ignored_files,
checksum: attributes.checksum,
bytes: attributes.bytes,
createdAt: new Date(attributes.created_at),
completedAt: attributes.completed_at ? new Date(attributes.completed_at) : null,
};
}
static toServerEggVariable ({ attributes }: FractalResponseData): Models.ServerEggVariable {
return {
name: attributes.name,
description: attributes.description,
envVariable: attributes.env_variable,
defaultValue: attributes.default_value,
serverValue: attributes.server_value,
isEditable: attributes.is_editable,
rules: attributes.rules.split('|'),
};
}
static toServerDatabase ({ attributes }: FractalResponseData): Models.ServerDatabase {
return {
id: attributes.id,
name: attributes.name,
username: attributes.username,
connectionString: `${attributes.host.address}:${attributes.host.port}`,
allowConnectionsFrom: attributes.connections_from,
// @ts-expect-error
password: attributes.relationships && attributes.relationships.password ? attributes.relationships.password.attributes.password : undefined,
};
}
static toSubuser ({ attributes }: FractalResponseData): Models.Subuser {
return {
uuid: attributes.uuid,
username: attributes.username,
email: attributes.email,
image: attributes.image,
twoFactorEnabled: attributes['2fa_enabled'],
createdAt: new Date(attributes.created_at),
permissions: attributes.permissions || [],
can: permission => (attributes.permissions || []).indexOf(permission) >= 0,
};
}
} }

View file

@ -1,24 +0,0 @@
import http, { getPaginationSet, PaginatedResult } from '@/api/http';
import { Transformers, Server } from '@definitions/user';
interface QueryParams {
query?: string;
page?: number;
type?: string;
}
export default ({ query, ...params }: QueryParams): Promise<PaginatedResult<Server>> => {
return new Promise((resolve, reject) => {
http.get('/api/client', {
params: {
'filter[*]': query,
...params,
},
})
.then(({ data }) => resolve({
items: (data.data || []).map(Transformers.toServer),
pagination: getPaginationSet(data.meta.pagination),
}))
.catch(reject);
});
};

View file

@ -1,10 +0,0 @@
import { PanelPermissions } from '@/state/permissions';
import http from '@/api/http';
export default (): Promise<PanelPermissions> => {
return new Promise((resolve, reject) => {
http.get('/api/client/permissions')
.then(({ data }) => resolve(data.attributes.permissions))
.catch(reject);
});
};

View file

@ -0,0 +1,47 @@
import http from '@/api/http';
import { ServerBackup, Transformers } from '@definitions/user';
const restoreServerBackup = async (uuid: string, backup: string, truncate?: boolean): Promise<void> => {
await http.post(`/api/client/servers/${uuid}/backups/${backup}/restore`, {
truncate,
});
};
interface CreateBackupParams {
name?: string;
ignored?: string;
isLocked: boolean;
}
const createServerBackup = async (uuid: string, params: CreateBackupParams): Promise<ServerBackup> => {
const { data } = await http.post(`/api/client/servers/${uuid}/backups`, {
name: params.name,
ignored: params.ignored,
is_locked: params.isLocked,
});
return Transformers.toServerBackup(data);
};
const deleteBackup = (uuid: string, backup: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/client/servers/${uuid}/backups/${backup}`)
.then(() => resolve())
.catch(reject);
});
};
const getBackupDownloadUrl = (uuid: string, backup: string): Promise<string> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/backups/${backup}/download`)
.then(({ data }) => resolve(data.attributes.url))
.catch(reject);
});
};
export {
deleteBackup,
restoreServerBackup,
createServerBackup,
getBackupDownloadUrl,
};

View file

@ -1,19 +0,0 @@
import http from '@/api/http';
import { ServerBackup } from '@/api/server/types';
import { rawDataToServerBackup } from '@/api/transformers';
interface RequestParameters {
name?: string;
ignored?: string;
isLocked: boolean;
}
export default async (uuid: string, params: RequestParameters): Promise<ServerBackup> => {
const { data } = await http.post(`/api/client/servers/${uuid}/backups`, {
name: params.name,
ignored: params.ignored,
is_locked: params.isLocked,
});
return rawDataToServerBackup(data);
};

View file

@ -1,9 +0,0 @@
import http from '@/api/http';
export default (uuid: string, backup: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/client/servers/${uuid}/backups/${backup}`)
.then(() => resolve())
.catch(reject);
});
};

View file

@ -1,9 +0,0 @@
import http from '@/api/http';
export default (uuid: string, backup: string): Promise<string> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/backups/${backup}/download`)
.then(({ data }) => resolve(data.attributes.url))
.catch(reject);
});
};

View file

@ -1,7 +0,0 @@
import http from '@/api/http';
export const restoreServerBackup = async (uuid: string, backup: string, truncate?: boolean): Promise<void> => {
await http.post(`/api/client/servers/${uuid}/backups/${backup}/restore`, {
truncate,
});
};

View file

@ -0,0 +1,50 @@
import http from '@/api/http';
import { Transformers, ServerDatabase } from '@definitions/user';
const createServerDatabase = (uuid: string, data: { connectionsFrom: string; databaseName: string }): Promise<ServerDatabase> => {
return new Promise((resolve, reject) => {
http.post(`/api/client/servers/${uuid}/databases`, {
database: data.databaseName,
remote: data.connectionsFrom,
}, {
params: { include: 'password' },
})
.then(response => resolve(Transformers.toServerDatabase(response.data.attributes)))
.catch(reject);
});
};
const getServerDatabases = (uuid: string, includePassword = true): Promise<ServerDatabase[]> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/databases`, {
params: includePassword ? { include: 'password' } : undefined,
})
.then(response => resolve(
(response.data.data || []).map((item: any) => Transformers.toServerDatabase(item.attributes))
))
.catch(reject);
});
};
const deleteServerDatabase = (uuid: string, database: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/client/servers/${uuid}/databases/${database}`)
.then(() => resolve())
.catch(reject);
});
};
const rotateDatabasePassword = (uuid: string, database: string): Promise<ServerDatabase> => {
return new Promise((resolve, reject) => {
http.post(`/api/client/servers/${uuid}/databases/${database}/rotate-password`)
.then((response) => resolve(Transformers.toServerDatabase(response.data)))
.catch(reject);
});
};
export {
createServerDatabase,
getServerDatabases,
deleteServerDatabase,
rotateDatabasePassword,
};

View file

@ -1,15 +0,0 @@
import { rawDataToServerDatabase, ServerDatabase } from '@/api/server/databases/getServerDatabases';
import http from '@/api/http';
export default (uuid: string, data: { connectionsFrom: string; databaseName: string }): Promise<ServerDatabase> => {
return new Promise((resolve, reject) => {
http.post(`/api/client/servers/${uuid}/databases`, {
database: data.databaseName,
remote: data.connectionsFrom,
}, {
params: { include: 'password' },
})
.then(response => resolve(rawDataToServerDatabase(response.data.attributes)))
.catch(reject);
});
};

View file

@ -1,9 +0,0 @@
import http from '@/api/http';
export default (uuid: string, database: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/client/servers/${uuid}/databases/${database}`)
.then(() => resolve())
.catch(reject);
});
};

View file

@ -1,31 +0,0 @@
import http from '@/api/http';
export interface ServerDatabase {
id: string;
name: string;
username: string;
connectionString: string;
allowConnectionsFrom: string;
password?: string;
}
export const rawDataToServerDatabase = (data: any): ServerDatabase => ({
id: data.id,
name: data.name,
username: data.username,
connectionString: `${data.host.address}:${data.host.port}`,
allowConnectionsFrom: data.connections_from,
password: data.relationships && data.relationships.password ? data.relationships.password.attributes.password : undefined,
});
export default (uuid: string, includePassword = true): Promise<ServerDatabase[]> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/databases`, {
params: includePassword ? { include: 'password' } : undefined,
})
.then(response => resolve(
(response.data.data || []).map((item: any) => rawDataToServerDatabase(item.attributes))
))
.catch(reject);
});
};

View file

@ -1,10 +0,0 @@
import { rawDataToServerDatabase, ServerDatabase } from '@/api/server/databases/getServerDatabases';
import http from '@/api/http';
export default (uuid: string, database: string): Promise<ServerDatabase> => {
return new Promise((resolve, reject) => {
http.post(`/api/client/servers/${uuid}/databases/${database}/rotate-password`)
.then((response) => resolve(rawDataToServerDatabase(response.data.attributes)))
.catch(reject);
});
};

View file

@ -1,7 +1,5 @@
import http from '@/api/http'; import http from '@/api/http';
import { Server, Transformers } from '@definitions/user'; import { Server, ServerEggVariable, Transformers } from '@definitions/user';
import { ServerEggVariable } from '@/api/server/types';
import { rawDataToServerEggVariable } from '@/api/transformers';
interface TokenResponse { interface TokenResponse {
token: string; token: string;
@ -55,7 +53,7 @@ const setSelectedDockerImage = async (uuid: string, image: string): Promise<void
const updateStartupVariable = async (uuid: string, key: string, value: string): Promise<[ ServerEggVariable, string ]> => { const updateStartupVariable = async (uuid: string, key: string, value: string): Promise<[ ServerEggVariable, string ]> => {
const { data } = await http.put(`/api/client/servers/${uuid}/startup/variable`, { key, value }); const { data } = await http.put(`/api/client/servers/${uuid}/startup/variable`, { key, value });
return [ rawDataToServerEggVariable(data), data.meta.startup_command ]; return [ Transformers.toServerEggVariable(data), data.meta.startup_command ];
}; };
const getServerResourceUsage = async (server: string): Promise<ServerStats> => { const getServerResourceUsage = async (server: string): Promise<ServerStats> => {

View file

@ -0,0 +1,30 @@
import http from '@/api/http';
import { Transformers, Allocation } from '@definitions/user';
const createServerAllocation = async (uuid: string): Promise<Allocation> => {
const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations`);
return Transformers.toServerAllocation(data);
};
const deleteServerAllocation = async (uuid: string, id: number): Promise<Allocation> =>
await http.delete(`/api/client/servers/${uuid}/network/allocations/${id}`);
const setPrimaryServerAllocation = async (uuid: string, id: number): Promise<Allocation> => {
const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations/${id}/primary`);
return Transformers.toServerAllocation(data);
};
const setServerAllocationNotes = async (uuid: string, id: number, notes: string | null): Promise<Allocation> => {
const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations/${id}`, { notes });
return Transformers.toServerAllocation(data);
};
export {
createServerAllocation,
deleteServerAllocation,
setPrimaryServerAllocation,
setServerAllocationNotes,
};

View file

@ -1,9 +0,0 @@
import { Allocation } from '@/api/server/getServer';
import http from '@/api/http';
import { rawDataToServerAllocation } from '@/api/transformers';
export default async (uuid: string): Promise<Allocation> => {
const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations`);
return rawDataToServerAllocation(data);
};

View file

@ -1,4 +0,0 @@
import { Allocation } from '@/api/server/getServer';
import http from '@/api/http';
export default async (uuid: string, id: number): Promise<Allocation> => await http.delete(`/api/client/servers/${uuid}/network/allocations/${id}`);

View file

@ -1,9 +0,0 @@
import { Allocation } from '@/api/server/getServer';
import http from '@/api/http';
import { rawDataToServerAllocation } from '@/api/transformers';
export default async (uuid: string, id: number): Promise<Allocation> => {
const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations/${id}/primary`);
return rawDataToServerAllocation(data);
};

View file

@ -1,9 +0,0 @@
import { Allocation } from '@/api/server/getServer';
import http from '@/api/http';
import { rawDataToServerAllocation } from '@/api/transformers';
export default async (uuid: string, id: number, notes: string | null): Promise<Allocation> => {
const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations/${id}`, { notes });
return rawDataToServerAllocation(data);
};

View file

@ -1,23 +0,0 @@
export type ServerStatus = 'installing' | 'install_failed' | 'suspended' | 'restoring_backup' | null;
export interface ServerBackup {
uuid: string;
isSuccessful: boolean;
isLocked: boolean;
name: string;
ignoredFiles: string;
checksum: string;
bytes: number;
createdAt: Date;
completedAt: Date | null;
}
export interface ServerEggVariable {
name: string;
description: string;
envVariable: string;
defaultValue: string;
serverValue: string;
isEditable: boolean;
rules: string[];
}

View file

@ -0,0 +1,39 @@
import http from '@/api/http';
import { Transformers, Subuser } from '@definitions/user';
interface Params {
email: string;
permissions: string[];
}
const deleteSubuser = (uuid: string, userId: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/client/servers/${uuid}/users/${userId}`)
.then(() => resolve())
.catch(reject);
});
};
const getServerSubusers = (uuid: string): Promise<Subuser[]> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/users`)
.then(({ data }) => resolve((data.data || []).map(Transformers.toSubuser)))
.catch(reject);
});
};
const createOrUpdateSubuser = (uuid: string, params: Params, subuser?: Subuser): Promise<Subuser> => {
return new Promise((resolve, reject) => {
http.post(`/api/client/servers/${uuid}/users${subuser ? `/${subuser.uuid}` : ''}`, {
...params,
})
.then(data => resolve(Transformers.toSubuser(data.data)))
.catch(reject);
});
};
export {
deleteSubuser,
getServerSubusers,
createOrUpdateSubuser,
};

View file

@ -1,18 +0,0 @@
import http from '@/api/http';
import { rawDataToServerSubuser } from '@/api/server/users/getServerSubusers';
import { Subuser } from '@/state/server/subusers';
interface Params {
email: string;
permissions: string[];
}
export default (uuid: string, params: Params, subuser?: Subuser): Promise<Subuser> => {
return new Promise((resolve, reject) => {
http.post(`/api/client/servers/${uuid}/users${subuser ? `/${subuser.uuid}` : ''}`, {
...params,
})
.then(data => resolve(rawDataToServerSubuser(data.data)))
.catch(reject);
});
};

View file

@ -1,9 +0,0 @@
import http from '@/api/http';
export default (uuid: string, userId: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/client/servers/${uuid}/users/${userId}`)
.then(() => resolve())
.catch(reject);
});
};

View file

@ -1,21 +0,0 @@
import http, { FractalResponseData } from '@/api/http';
import { Subuser } from '@/state/server/subusers';
export const rawDataToServerSubuser = (data: FractalResponseData): Subuser => ({
uuid: data.attributes.uuid,
username: data.attributes.username,
email: data.attributes.email,
image: data.attributes.image,
twoFactorEnabled: data.attributes['2fa_enabled'],
createdAt: new Date(data.attributes.created_at),
permissions: data.attributes.permissions || [],
can: permission => (data.attributes.permissions || []).indexOf(permission) >= 0,
});
export default (uuid: string): Promise<Subuser[]> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/users`)
.then(({ data }) => resolve((data.data || []).map(rawDataToServerSubuser)))
.catch(reject);
});
};

View file

@ -1,8 +1,7 @@
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import useSWR from 'swr'; import useSWR from 'swr';
import http from '@/api/http'; import http from '@/api/http';
import { rawDataToServerAllocation } from '@/api/transformers'; import { Allocation, Transformers } from '@definitions/user';
import { Allocation } from '@/api/server/getServer';
export default () => { export default () => {
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
@ -10,6 +9,6 @@ export default () => {
return useSWR<Allocation[]>([ 'server:allocations', uuid ], async () => { return useSWR<Allocation[]>([ 'server:allocations', uuid ], async () => {
const { data } = await http.get(`/api/client/servers/${uuid}/network/allocations`); const { data } = await http.get(`/api/client/servers/${uuid}/network/allocations`);
return (data.data || []).map(rawDataToServerAllocation); return (data.data || []).map(Transformers.toServerAllocation);
}, { revalidateOnFocus: false, revalidateOnMount: false }); }, { revalidateOnFocus: false, revalidateOnMount: false });
}; };

View file

@ -1,7 +1,6 @@
import useSWR from 'swr'; import useSWR from 'swr';
import http, { getPaginationSet, PaginatedResult } from '@/api/http'; import http, { getPaginationSet, PaginatedResult } from '@/api/http';
import { ServerBackup } from '@/api/server/types'; import { Transformers, ServerBackup } from '@definitions/user';
import { rawDataToServerBackup } from '@/api/transformers';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import { createContext, useContext } from 'react'; import { createContext, useContext } from 'react';
@ -22,7 +21,7 @@ export default () => {
const { data } = await http.get(`/api/client/servers/${uuid}/backups`, { params: { page } }); const { data } = await http.get(`/api/client/servers/${uuid}/backups`, { params: { page } });
return ({ return ({
items: (data.data || []).map(rawDataToServerBackup), items: (data.data || []).map(Transformers.toServerBackup),
pagination: getPaginationSet(data.meta.pagination), pagination: getPaginationSet(data.meta.pagination),
backupCount: data.meta.backup_count, backupCount: data.meta.backup_count,
}); });

View file

@ -1,7 +1,6 @@
import useSWR from 'swr'; import useSWR from 'swr';
import http, { FractalResponseList } from '@/api/http'; import http, { FractalResponseList } from '@/api/http';
import { rawDataToServerEggVariable } from '@/api/transformers'; import { Transformers, ServerEggVariable } from '@definitions/user';
import { ServerEggVariable } from '@/api/server/types';
interface Response { interface Response {
invocation: string; invocation: string;
@ -12,7 +11,7 @@ interface Response {
export default (uuid: string, initialData?: Response) => useSWR([ uuid, '/startup' ], async (): Promise<Response> => { export default (uuid: string, initialData?: Response) => useSWR([ uuid, '/startup' ], async (): Promise<Response> => {
const { data } = await http.get(`/api/client/servers/${uuid}/startup`); const { data } = await http.get(`/api/client/servers/${uuid}/startup`);
const variables = ((data as FractalResponseList).data || []).map(rawDataToServerEggVariable); const variables = ((data as FractalResponseList).data || []).map(Transformers.toServerEggVariable);
return { invocation: data.meta.startup_command, variables, dockerImages: data.meta.docker_images || [] }; return { invocation: data.meta.startup_command, variables, dockerImages: data.meta.docker_images || [] };
}, { fallbackData: initialData, errorRetryCount: 3 }); }, { fallbackData: initialData, errorRetryCount: 3 });

View file

@ -1,16 +1,6 @@
import { FractalResponseData } from '@/api/http'; import { FractalResponseData } from '@/api/http';
import { Allocation } from '@/api/server/getServer';
import { FileObject } from '@/api/server/files/loadDirectory'; import { FileObject } from '@/api/server/files/loadDirectory';
import { ServerBackup, ServerEggVariable } from '@/api/server/types'; import { Transformers } from '@definitions/user';
export const rawDataToServerAllocation = (data: FractalResponseData): Allocation => ({
id: data.attributes.id,
ip: data.attributes.ip,
alias: data.attributes.ip_alias,
port: data.attributes.port,
notes: data.attributes.notes,
isDefault: data.attributes.is_default,
});
export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({
key: `${data.attributes.is_file ? 'file' : 'dir'}_${data.attributes.name}`, key: `${data.attributes.is_file ? 'file' : 'dir'}_${data.attributes.name}`,
@ -55,24 +45,4 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({
}, },
}); });
export const rawDataToServerBackup = ({ attributes }: FractalResponseData): ServerBackup => ({ export const rawDataToServerAllocation = Transformers.toServerAllocation;
uuid: attributes.uuid,
isSuccessful: attributes.is_successful,
isLocked: attributes.is_locked,
name: attributes.name,
ignoredFiles: attributes.ignored_files,
checksum: attributes.checksum,
bytes: attributes.bytes,
createdAt: new Date(attributes.created_at),
completedAt: attributes.completed_at ? new Date(attributes.completed_at) : null,
});
export const rawDataToServerEggVariable = ({ attributes }: FractalResponseData): ServerEggVariable => ({
name: attributes.name,
description: attributes.description,
envVariable: attributes.env_variable,
defaultValue: attributes.default_value,
serverValue: attributes.server_value,
isEditable: attributes.is_editable,
rules: attributes.rules.split('|'),
});

View file

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Server } from '@definitions/user'; import { Server } from '@definitions/user';
import getServers from '@/api/getServers'; import { getServers } from '@/api/account';
import ServerRow from '@/components/dashboard/ServerRow'; import ServerRow from '@/components/dashboard/ServerRow';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
import PageContentBlock from '@/components/elements/PageContentBlock'; import PageContentBlock from '@/components/elements/PageContentBlock';

View file

@ -4,7 +4,7 @@ import { Form, Formik, FormikHelpers } from 'formik';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import * as Yup from 'yup'; import * as Yup from 'yup';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import updateAccountPassword from '@/api/account/updateAccountPassword'; import { updateAccountPassword } from '@/api/account';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import tw from 'twin.macro'; import tw from 'twin.macro';

View file

@ -6,7 +6,7 @@ import { object, string } from 'yup';
import debounce from 'debounce'; import debounce from 'debounce';
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper'; import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
import InputSpinner from '@/components/elements/InputSpinner'; import InputSpinner from '@/components/elements/InputSpinner';
import getServers from '@/api/getServers'; import { getServers } from '@/api/account';
import { Server } from '@definitions/user'; import { Server } from '@definitions/user';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';

View file

@ -1,19 +1,24 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { faBoxOpen, faCloudDownloadAlt, faEllipsisH, faLock, faTrashAlt, faUnlock } from '@fortawesome/free-solid-svg-icons'; import {
faBoxOpen,
faCloudDownloadAlt,
faEllipsisH,
faLock,
faTrashAlt,
faUnlock,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import DropdownMenu, { DropdownButtonRow } from '@/components/elements/DropdownMenu'; import DropdownMenu, { DropdownButtonRow } from '@/components/elements/DropdownMenu';
import getBackupDownloadUrl from '@/api/server/backups/getBackupDownloadUrl'; import { getBackupDownloadUrl, deleteBackup, restoreServerBackup } from '@/api/server/backups';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import deleteBackup from '@/api/server/backups/deleteBackup';
import ConfirmationModal from '@/components/elements/ConfirmationModal'; import ConfirmationModal from '@/components/elements/ConfirmationModal';
import Can from '@/components/elements/Can'; import Can from '@/components/elements/Can';
import tw from 'twin.macro'; import tw from 'twin.macro';
import getServerBackups from '@/api/swr/getServerBackups'; import getServerBackups from '@/api/swr/getServerBackups';
import { ServerBackup } from '@/api/server/types'; import { ServerBackup } from '@definitions/user';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import Input from '@/components/elements/Input'; import Input from '@/components/elements/Input';
import { restoreServerBackup } from '@/api/server/backups';
import http, { httpErrorToHuman } from '@/api/http'; import http, { httpErrorToHuman } from '@/api/http';
interface Props { interface Props {

View file

@ -10,7 +10,7 @@ import BackupContextMenu from '@/components/server/backups/BackupContextMenu';
import tw from 'twin.macro'; import tw from 'twin.macro';
import GreyRowBox from '@/components/elements/GreyRowBox'; import GreyRowBox from '@/components/elements/GreyRowBox';
import getServerBackups from '@/api/swr/getServerBackups'; import getServerBackups from '@/api/swr/getServerBackups';
import { ServerBackup } from '@/api/server/types'; import { ServerBackup } from '@definitions/user';
import { SocketEvent } from '@/components/server/events'; import { SocketEvent } from '@/components/server/events';
interface Props { interface Props {

View file

@ -5,7 +5,7 @@ import { boolean, object, string } from 'yup';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper'; import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
import createServerBackup from '@/api/server/backups/createServerBackup'; import { createServerBackup } from '@/api/server/backups';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import tw from 'twin.macro'; import tw from 'twin.macro';

View file

@ -3,7 +3,7 @@ import Modal from '@/components/elements/Modal';
import { Form, Formik, FormikHelpers } from 'formik'; import { Form, Formik, FormikHelpers } from 'formik';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import { object, string } from 'yup'; import { object, string } from 'yup';
import createServerDatabase from '@/api/server/databases/createServerDatabase'; import { createServerDatabase } from '@/api/server/databases';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';

View file

@ -7,11 +7,11 @@ import Field from '@/components/elements/Field';
import { object, string } from 'yup'; import { object, string } from 'yup';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import deleteServerDatabase from '@/api/server/databases/deleteServerDatabase'; import { deleteServerDatabase } from '@/api/server/databases';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import RotatePasswordButton from '@/components/server/databases/RotatePasswordButton'; import RotatePasswordButton from '@/components/server/databases/RotatePasswordButton';
import Can from '@/components/elements/Can'; import Can from '@/components/elements/Can';
import { ServerDatabase } from '@/api/server/databases/getServerDatabases'; import { ServerDatabase } from '@definitions/user';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
import tw from 'twin.macro'; import tw from 'twin.macro';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
@ -114,7 +114,12 @@ export default ({ database, className }: Props) => {
<h3 css={tw`mb-6 text-2xl`}>Database connection details</h3> <h3 css={tw`mb-6 text-2xl`}>Database connection details</h3>
<div> <div>
<Label>Endpoint</Label> <Label>Endpoint</Label>
<CopyOnClick text={database.connectionString}><Input type={'text'} readOnly value={database.connectionString} /></CopyOnClick> <CopyOnClick text={database.connectionString}><Input
type={'text'}
readOnly
value={database.connectionString}
/>
</CopyOnClick>
</div> </div>
<div css={tw`mt-6`}> <div css={tw`mt-6`}>
<Label>Connections from</Label> <Label>Connections from</Label>
@ -122,7 +127,12 @@ export default ({ database, className }: Props) => {
</div> </div>
<div css={tw`mt-6`}> <div css={tw`mt-6`}>
<Label>Username</Label> <Label>Username</Label>
<CopyOnClick text={database.username}><Input type={'text'} readOnly value={database.username} /></CopyOnClick> <CopyOnClick text={database.username}><Input
type={'text'}
readOnly
value={database.username}
/>
</CopyOnClick>
</div> </div>
<Can action={'database.view_password'}> <Can action={'database.view_password'}>
<div css={tw`mt-6`}> <div css={tw`mt-6`}>
@ -157,7 +167,8 @@ export default ({ database, className }: Props) => {
<CopyOnClick text={database.name}><p css={tw`text-lg`}>{database.name}</p></CopyOnClick> <CopyOnClick text={database.name}><p css={tw`text-lg`}>{database.name}</p></CopyOnClick>
</div> </div>
<div css={tw`ml-8 text-center hidden md:block`}> <div css={tw`ml-8 text-center hidden md:block`}>
<CopyOnClick text={database.connectionString}><p css={tw`text-sm`}>{database.connectionString}</p></CopyOnClick> <CopyOnClick text={database.connectionString}><p css={tw`text-sm`}>{database.connectionString}</p>
</CopyOnClick>
<p css={tw`mt-1 text-2xs text-neutral-500 uppercase select-none`}>Endpoint</p> <p css={tw`mt-1 text-2xs text-neutral-500 uppercase select-none`}>Endpoint</p>
</div> </div>
<div css={tw`ml-8 text-center hidden md:block`}> <div css={tw`ml-8 text-center hidden md:block`}>

View file

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import getServerDatabases from '@/api/server/databases/getServerDatabases'; import { getServerDatabases } from '@/api/server/databases';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';

View file

@ -1,9 +1,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import rotateDatabasePassword from '@/api/server/databases/rotateDatabasePassword'; import { rotateDatabasePassword } from '@/api/server/databases';
import { Actions, useStoreActions } from 'easy-peasy'; import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import { ServerDatabase } from '@/api/server/databases/getServerDatabases'; import { ServerDatabase } from '@definitions/user';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import tw from 'twin.macro'; import tw from 'twin.macro';

View file

@ -10,12 +10,11 @@ import Button from '@/components/elements/Button';
import GreyRowBox from '@/components/elements/GreyRowBox'; import GreyRowBox from '@/components/elements/GreyRowBox';
import { Allocation } from '@/api/server/getServer'; import { Allocation } from '@/api/server/getServer';
import { debounce } from 'debounce'; import { debounce } from 'debounce';
import setServerAllocationNotes from '@/api/server/network/setServerAllocationNotes'; import { setServerAllocationNotes, setPrimaryServerAllocation } from '@/api/server/network';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import CopyOnClick from '@/components/elements/CopyOnClick'; import CopyOnClick from '@/components/elements/CopyOnClick';
import DeleteAllocationButton from '@/components/server/network/DeleteAllocationButton'; import DeleteAllocationButton from '@/components/server/network/DeleteAllocationButton';
import setPrimaryServerAllocation from '@/api/server/network/setPrimaryServerAllocation';
import getServerAllocations from '@/api/swr/getServerAllocations'; import getServerAllocations from '@/api/swr/getServerAllocations';
import { formatIp } from '@/helpers'; import { formatIp } from '@/helpers';

View file

@ -4,7 +4,7 @@ import tw from 'twin.macro';
import Icon from '@/components/elements/Icon'; import Icon from '@/components/elements/Icon';
import ConfirmationModal from '@/components/elements/ConfirmationModal'; import ConfirmationModal from '@/components/elements/ConfirmationModal';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import deleteServerAllocation from '@/api/server/network/deleteServerAllocation'; import { deleteServerAllocation } from '@/api/server/network';
import getServerAllocations from '@/api/swr/getServerAllocations'; import getServerAllocations from '@/api/swr/getServerAllocations';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';

View file

@ -5,7 +5,7 @@ import ServerContentBlock from '@/components/elements/ServerContentBlock';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import AllocationRow from '@/components/server/network/AllocationRow'; import AllocationRow from '@/components/server/network/AllocationRow';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import createServerAllocation from '@/api/server/network/createServerAllocation'; import { createServerAllocation } from '@/api/server/network';
import tw from 'twin.macro'; import tw from 'twin.macro';
import Can from '@/components/elements/Can'; import Can from '@/components/elements/Can';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay';

View file

@ -1,5 +1,5 @@
import React, { memo, useState } from 'react'; import React, { memo, useState } from 'react';
import { ServerEggVariable } from '@/api/server/types'; import { ServerEggVariable } from '@definitions/user';
import TitledGreyBox from '@/components/elements/TitledGreyBox'; import TitledGreyBox from '@/components/elements/TitledGreyBox';
import { usePermissions } from '@/plugins/usePermissions'; import { usePermissions } from '@/plugins/usePermissions';
import InputSpinner from '@/components/elements/InputSpinner'; import InputSpinner from '@/components/elements/InputSpinner';

View file

@ -5,7 +5,7 @@ import { array, object, string } from 'yup';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import { Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { Actions, useStoreActions, useStoreState } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser'; import { createOrUpdateSubuser } from '@/api/server/users';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import Can from '@/components/elements/Can'; import Can from '@/components/elements/Can';

View file

@ -4,7 +4,7 @@ import { ServerContext } from '@/state/server';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { Subuser } from '@/state/server/subusers'; import { Subuser } from '@/state/server/subusers';
import deleteSubuser from '@/api/server/users/deleteSubuser'; import { deleteSubuser } from '@/api/server/users';
import { Actions, useStoreActions } from 'easy-peasy'; import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';

View file

@ -6,7 +6,7 @@ import Spinner from '@/components/elements/Spinner';
import AddSubuserButton from '@/components/server/users/AddSubuserButton'; import AddSubuserButton from '@/components/server/users/AddSubuserButton';
import UserRow from '@/components/server/users/UserRow'; import UserRow from '@/components/server/users/UserRow';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import getServerSubusers from '@/api/server/users/getServerSubusers'; import { getServerSubusers } from '@/api/server/users';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import Can from '@/components/elements/Can'; import Can from '@/components/elements/Can';
import ServerContentBlock from '@/components/elements/ServerContentBlock'; import ServerContentBlock from '@/components/elements/ServerContentBlock';

View file

@ -1,5 +1,5 @@
import { action, Action, thunk, Thunk } from 'easy-peasy'; import { action, Action, thunk, Thunk } from 'easy-peasy';
import getSystemPermissions from '@/api/getSystemPermissions'; import { getSystemPermissions } from '@/api/account';
export interface PanelPermissions { export interface PanelPermissions {
[key: string]: { [key: string]: {

View file

@ -1,5 +1,5 @@
import { action, Action } from 'easy-peasy'; import { action, Action } from 'easy-peasy';
import { ServerDatabase } from '@/api/server/databases/getServerDatabases'; import { ServerDatabase } from '@definitions/user';
export interface ServerDatabaseStore { export interface ServerDatabaseStore {
data: ServerDatabase[]; data: ServerDatabase[];

View file

@ -1,5 +1,5 @@
import { Action, action, Thunk, thunk } from 'easy-peasy'; import { Action, action, Thunk, thunk } from 'easy-peasy';
import updateAccountEmail from '@/api/account/updateAccountEmail'; import { updateAccountEmail } from '@/api/account';
export interface UserData { export interface UserData {
uuid: string; uuid: string;