1077 lines
37 KiB
TypeScript
1077 lines
37 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
import * as interfaces from '../ts_interfaces/index.js';
|
|
|
|
// ============================================================================
|
|
// Smartstate instance
|
|
// ============================================================================
|
|
export const appState = new plugins.domtools.plugins.smartstate.Smartstate();
|
|
|
|
// ============================================================================
|
|
// State Part Interfaces
|
|
// ============================================================================
|
|
|
|
export interface ILoginState {
|
|
identity: interfaces.data.IIdentity | null;
|
|
isLoggedIn: boolean;
|
|
}
|
|
|
|
export interface IConnectionsState {
|
|
connections: interfaces.data.IProviderConnection[];
|
|
activeConnectionId: string | null;
|
|
}
|
|
|
|
export interface IDataState {
|
|
projects: interfaces.data.IProject[];
|
|
groups: interfaces.data.IGroup[];
|
|
secrets: interfaces.data.ISecret[];
|
|
pipelines: interfaces.data.IPipeline[];
|
|
pipelineJobs: interfaces.data.IPipelineJob[];
|
|
currentJobLog: string;
|
|
}
|
|
|
|
export interface IActionLogState {
|
|
entries: interfaces.data.IActionLogEntry[];
|
|
total: number;
|
|
}
|
|
|
|
export interface INavigationContext {
|
|
connectionId?: string;
|
|
scope?: 'project' | 'group';
|
|
scopeId?: string;
|
|
projectId?: string;
|
|
}
|
|
|
|
export interface IUiState {
|
|
activeView: string;
|
|
autoRefresh: boolean;
|
|
refreshInterval: number;
|
|
navigationContext?: INavigationContext;
|
|
}
|
|
|
|
// ============================================================================
|
|
// State Parts
|
|
// ============================================================================
|
|
|
|
export const loginStatePart = await appState.getStatePart<ILoginState>(
|
|
'login',
|
|
{
|
|
identity: null,
|
|
isLoggedIn: false,
|
|
},
|
|
'persistent',
|
|
);
|
|
|
|
export const connectionsStatePart = await appState.getStatePart<IConnectionsState>(
|
|
'connections',
|
|
{
|
|
connections: [],
|
|
activeConnectionId: null,
|
|
},
|
|
'soft',
|
|
);
|
|
|
|
export const dataStatePart = await appState.getStatePart<IDataState>(
|
|
'data',
|
|
{
|
|
projects: [],
|
|
groups: [],
|
|
secrets: [],
|
|
pipelines: [],
|
|
pipelineJobs: [],
|
|
currentJobLog: '',
|
|
},
|
|
'soft',
|
|
);
|
|
|
|
export const actionLogStatePart = await appState.getStatePart<IActionLogState>(
|
|
'actionLog',
|
|
{
|
|
entries: [],
|
|
total: 0,
|
|
},
|
|
'soft',
|
|
);
|
|
|
|
export const uiStatePart = await appState.getStatePart<IUiState>(
|
|
'ui',
|
|
{
|
|
activeView: 'overview',
|
|
autoRefresh: true,
|
|
refreshInterval: 30000,
|
|
},
|
|
);
|
|
|
|
// ============================================================================
|
|
// Helpers
|
|
// ============================================================================
|
|
|
|
interface IActionContext {
|
|
identity: interfaces.data.IIdentity | null;
|
|
}
|
|
|
|
const getActionContext = (): IActionContext => {
|
|
return { identity: loginStatePart.getState().identity };
|
|
};
|
|
|
|
// ============================================================================
|
|
// Login Actions
|
|
// ============================================================================
|
|
|
|
export const loginAction = loginStatePart.createAction<{
|
|
username: string;
|
|
password: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_AdminLogin
|
|
>('/typedrequest', 'adminLogin');
|
|
|
|
const response = await typedRequest.fire({
|
|
username: dataArg.username,
|
|
password: dataArg.password,
|
|
});
|
|
|
|
return {
|
|
identity: response.identity || null,
|
|
isLoggedIn: !!response.identity,
|
|
};
|
|
} catch (err) {
|
|
console.error('Login failed:', err);
|
|
return { identity: null, isLoggedIn: false };
|
|
}
|
|
});
|
|
|
|
export const logoutAction = loginStatePart.createAction(async (_statePartArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
if (context.identity) {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_AdminLogout
|
|
>('/typedrequest', 'adminLogout');
|
|
await typedRequest.fire({ identity: context.identity });
|
|
}
|
|
} catch (err) {
|
|
console.error('Logout error:', err);
|
|
}
|
|
return { identity: null, isLoggedIn: false };
|
|
});
|
|
|
|
// ============================================================================
|
|
// Connections Actions
|
|
// ============================================================================
|
|
|
|
export const fetchConnectionsAction = connectionsStatePart.createAction(async (statePartArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetConnections
|
|
>('/typedrequest', 'getConnections');
|
|
const response = await typedRequest.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), connections: response.connections };
|
|
} catch (err) {
|
|
console.error('Failed to fetch connections:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const createConnectionAction = connectionsStatePart.createAction<{
|
|
name: string;
|
|
providerType: interfaces.data.TProviderType;
|
|
baseUrl: string;
|
|
token: string;
|
|
groupFilter?: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_CreateConnection
|
|
>('/typedrequest', 'createConnection');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetConnections
|
|
>('/typedrequest', 'getConnections');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), connections: listResp.connections };
|
|
} catch (err) {
|
|
console.error('Failed to create connection:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const testConnectionAction = connectionsStatePart.createAction<{
|
|
connectionId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_TestConnection
|
|
>('/typedrequest', 'testConnection');
|
|
const result = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
});
|
|
// Re-fetch to get updated status
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetConnections
|
|
>('/typedrequest', 'getConnections');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), connections: listResp.connections };
|
|
} catch (err) {
|
|
console.error('Failed to test connection:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const deleteConnectionAction = connectionsStatePart.createAction<{
|
|
connectionId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_DeleteConnection
|
|
>('/typedrequest', 'deleteConnection');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
});
|
|
const state = statePartArg.getState();
|
|
return {
|
|
...state,
|
|
connections: state.connections.filter((c) => c.id !== dataArg.connectionId),
|
|
activeConnectionId: state.activeConnectionId === dataArg.connectionId ? null : state.activeConnectionId,
|
|
};
|
|
} catch (err) {
|
|
console.error('Failed to delete connection:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const pauseConnectionAction = connectionsStatePart.createAction<{
|
|
connectionId: string;
|
|
paused: boolean;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_PauseConnection
|
|
>('/typedrequest', 'pauseConnection');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch to get updated status
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetConnections
|
|
>('/typedrequest', 'getConnections');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), connections: listResp.connections };
|
|
} catch (err) {
|
|
console.error('Failed to pause/resume connection:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const updateConnectionAction = connectionsStatePart.createAction<{
|
|
connectionId: string;
|
|
name?: string;
|
|
baseUrl?: string;
|
|
token?: string;
|
|
groupFilter?: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_UpdateConnection
|
|
>('/typedrequest', 'updateConnection');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch to get updated data
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetConnections
|
|
>('/typedrequest', 'getConnections');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), connections: listResp.connections };
|
|
} catch (err) {
|
|
console.error('Failed to update connection:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Projects Actions
|
|
// ============================================================================
|
|
|
|
export const fetchProjectsAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
search?: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetProjects
|
|
>('/typedrequest', 'getProjects');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
search: dataArg.search,
|
|
});
|
|
return { ...statePartArg.getState(), projects: response.projects };
|
|
} catch (err) {
|
|
console.error('Failed to fetch projects:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Groups Actions
|
|
// ============================================================================
|
|
|
|
export const fetchGroupsAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
search?: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetGroups
|
|
>('/typedrequest', 'getGroups');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
search: dataArg.search,
|
|
});
|
|
return { ...statePartArg.getState(), groups: response.groups };
|
|
} catch (err) {
|
|
console.error('Failed to fetch groups:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Secrets Actions
|
|
// ============================================================================
|
|
|
|
export const fetchSecretsAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
scope: 'project' | 'group';
|
|
scopeId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSecrets
|
|
>('/typedrequest', 'getSecrets');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
scope: dataArg.scope,
|
|
scopeId: dataArg.scopeId,
|
|
});
|
|
return { ...statePartArg.getState(), secrets: response.secrets };
|
|
} catch (err) {
|
|
console.error('Failed to fetch secrets:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const fetchAllSecretsAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
scope?: 'project' | 'group';
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
// When no scope specified, fetch both project and group secrets in parallel
|
|
const scopes: Array<'project' | 'group'> = dataArg.scope ? [dataArg.scope] : ['project', 'group'];
|
|
const results = await Promise.all(
|
|
scopes.map(async (scope) => {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetAllSecrets
|
|
>('/typedrequest', 'getAllSecrets');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
scope,
|
|
});
|
|
return response.secrets;
|
|
}),
|
|
);
|
|
return { ...statePartArg.getState(), secrets: results.flat() };
|
|
} catch (err) {
|
|
console.error('Failed to fetch all secrets:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const createSecretAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
scope: 'project' | 'group';
|
|
scopeId: string;
|
|
key: string;
|
|
value: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_CreateSecret
|
|
>('/typedrequest', 'createSecret');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch only the affected entity's secrets and merge
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSecrets
|
|
>('/typedrequest', 'getSecrets');
|
|
const listResp = await listReq.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
scope: dataArg.scope,
|
|
scopeId: dataArg.scopeId,
|
|
});
|
|
const state = statePartArg.getState();
|
|
const otherSecrets = state.secrets.filter(
|
|
(s) => !(s.scopeId === dataArg.scopeId && s.scope === dataArg.scope),
|
|
);
|
|
return { ...state, secrets: [...otherSecrets, ...listResp.secrets] };
|
|
} catch (err) {
|
|
console.error('Failed to create secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const updateSecretAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
scope: 'project' | 'group';
|
|
scopeId: string;
|
|
key: string;
|
|
value: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_UpdateSecret
|
|
>('/typedrequest', 'updateSecret');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch only the affected entity's secrets and merge
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSecrets
|
|
>('/typedrequest', 'getSecrets');
|
|
const listResp = await listReq.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
scope: dataArg.scope,
|
|
scopeId: dataArg.scopeId,
|
|
});
|
|
const state = statePartArg.getState();
|
|
const otherSecrets = state.secrets.filter(
|
|
(s) => !(s.scopeId === dataArg.scopeId && s.scope === dataArg.scope),
|
|
);
|
|
return { ...state, secrets: [...otherSecrets, ...listResp.secrets] };
|
|
} catch (err) {
|
|
console.error('Failed to update secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const deleteSecretAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
scope: 'project' | 'group';
|
|
scopeId: string;
|
|
key: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_DeleteSecret
|
|
>('/typedrequest', 'deleteSecret');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
const state = statePartArg.getState();
|
|
return {
|
|
...state,
|
|
secrets: state.secrets.filter(
|
|
(s) => !(s.key === dataArg.key && s.scopeId === dataArg.scopeId && s.scope === dataArg.scope),
|
|
),
|
|
};
|
|
} catch (err) {
|
|
console.error('Failed to delete secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Pipelines Actions
|
|
// ============================================================================
|
|
|
|
export const fetchPipelinesAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
projectId?: string;
|
|
viewMode?: 'current' | 'project' | 'group' | 'error';
|
|
groupId?: string;
|
|
status?: string;
|
|
sortBy?: 'created' | 'duration' | 'status';
|
|
timeRange?: '1h' | '6h' | '1d' | '3d' | '7d' | '30d';
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetPipelines
|
|
>('/typedrequest', 'getPipelines');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
projectId: dataArg.projectId,
|
|
viewMode: dataArg.viewMode,
|
|
groupId: dataArg.groupId,
|
|
status: dataArg.status,
|
|
sortBy: dataArg.sortBy,
|
|
timeRange: dataArg.timeRange,
|
|
});
|
|
return { ...statePartArg.getState(), pipelines: response.pipelines };
|
|
} catch (err) {
|
|
console.error('Failed to fetch pipelines:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const fetchPipelineJobsAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
projectId: string;
|
|
pipelineId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetPipelineJobs
|
|
>('/typedrequest', 'getPipelineJobs');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
projectId: dataArg.projectId,
|
|
pipelineId: dataArg.pipelineId,
|
|
});
|
|
return { ...statePartArg.getState(), pipelineJobs: response.jobs };
|
|
} catch (err) {
|
|
console.error('Failed to fetch pipeline jobs:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const retryPipelineAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
projectId: string;
|
|
pipelineId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_RetryPipeline
|
|
>('/typedrequest', 'retryPipeline');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch pipelines
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetPipelines
|
|
>('/typedrequest', 'getPipelines');
|
|
const listResp = await listReq.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
projectId: dataArg.projectId,
|
|
});
|
|
return { ...statePartArg.getState(), pipelines: listResp.pipelines };
|
|
} catch (err) {
|
|
console.error('Failed to retry pipeline:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const cancelPipelineAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
projectId: string;
|
|
pipelineId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_CancelPipeline
|
|
>('/typedrequest', 'cancelPipeline');
|
|
await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
// Re-fetch pipelines
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetPipelines
|
|
>('/typedrequest', 'getPipelines');
|
|
const listResp = await listReq.fire({
|
|
identity: context.identity!,
|
|
connectionId: dataArg.connectionId,
|
|
projectId: dataArg.projectId,
|
|
});
|
|
return { ...statePartArg.getState(), pipelines: listResp.pipelines };
|
|
} catch (err) {
|
|
console.error('Failed to cancel pipeline:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Logs Actions
|
|
// ============================================================================
|
|
|
|
export const fetchJobLogAction = dataStatePart.createAction<{
|
|
connectionId: string;
|
|
projectId: string;
|
|
jobId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetJobLog
|
|
>('/typedrequest', 'getJobLog');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
...dataArg,
|
|
});
|
|
return { ...statePartArg.getState(), currentJobLog: response.log };
|
|
} catch (err) {
|
|
console.error('Failed to fetch job log:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Action Log Actions
|
|
// ============================================================================
|
|
|
|
export const fetchActionLogAction = actionLogStatePart.createAction<{
|
|
limit?: number;
|
|
offset?: number;
|
|
entityType?: interfaces.data.TActionEntity;
|
|
} | null>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetActionLog
|
|
>('/typedrequest', 'getActionLog');
|
|
const response = await typedRequest.fire({
|
|
identity: context.identity!,
|
|
limit: dataArg?.limit,
|
|
offset: dataArg?.offset,
|
|
entityType: dataArg?.entityType,
|
|
});
|
|
return { entries: response.entries, total: response.total };
|
|
} catch (err) {
|
|
console.error('Failed to fetch action log:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// UI Actions
|
|
// ============================================================================
|
|
|
|
export const setActiveViewAction = uiStatePart.createAction<{
|
|
view: string;
|
|
navigationContext?: INavigationContext;
|
|
}>(
|
|
async (statePartArg, dataArg) => {
|
|
return {
|
|
...statePartArg.getState(),
|
|
activeView: dataArg.view,
|
|
navigationContext: dataArg.navigationContext,
|
|
};
|
|
},
|
|
);
|
|
|
|
export const clearNavigationContextAction = uiStatePart.createAction(
|
|
async (statePartArg) => {
|
|
return { ...statePartArg.getState(), navigationContext: undefined };
|
|
},
|
|
);
|
|
|
|
export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg) => {
|
|
const state = statePartArg.getState();
|
|
return { ...state, autoRefresh: !state.autoRefresh };
|
|
});
|
|
|
|
export const setRefreshIntervalAction = uiStatePart.createAction<{ interval: number }>(
|
|
async (statePartArg, dataArg) => {
|
|
return { ...statePartArg.getState(), refreshInterval: dataArg.interval };
|
|
},
|
|
);
|
|
|
|
// ============================================================================
|
|
// Managed Secrets State
|
|
// ============================================================================
|
|
|
|
export interface IManagedSecretsState {
|
|
managedSecrets: interfaces.data.IManagedSecret[];
|
|
}
|
|
|
|
export const managedSecretsStatePart = await appState.getStatePart<IManagedSecretsState>(
|
|
'managedSecrets',
|
|
{ managedSecrets: [] },
|
|
'soft',
|
|
);
|
|
|
|
export const fetchManagedSecretsAction = managedSecretsStatePart.createAction(
|
|
async (statePartArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetManagedSecrets
|
|
>('/typedrequest', 'getManagedSecrets');
|
|
const response = await typedRequest.fire({ identity: context.identity! });
|
|
return { managedSecrets: response.managedSecrets };
|
|
} catch (err) {
|
|
console.error('Failed to fetch managed secrets:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
},
|
|
);
|
|
|
|
export const createManagedSecretAction = managedSecretsStatePart.createAction<{
|
|
key: string;
|
|
value: string;
|
|
description?: string;
|
|
targets: interfaces.data.IManagedSecretTarget[];
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_CreateManagedSecret
|
|
>('/typedrequest', 'createManagedSecret');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
// Re-fetch
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetManagedSecrets
|
|
>('/typedrequest', 'getManagedSecrets');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { managedSecrets: listResp.managedSecrets };
|
|
} catch (err) {
|
|
console.error('Failed to create managed secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const updateManagedSecretAction = managedSecretsStatePart.createAction<{
|
|
managedSecretId: string;
|
|
value?: string;
|
|
description?: string;
|
|
targets?: interfaces.data.IManagedSecretTarget[];
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_UpdateManagedSecret
|
|
>('/typedrequest', 'updateManagedSecret');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetManagedSecrets
|
|
>('/typedrequest', 'getManagedSecrets');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { managedSecrets: listResp.managedSecrets };
|
|
} catch (err) {
|
|
console.error('Failed to update managed secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const deleteManagedSecretAction = managedSecretsStatePart.createAction<{
|
|
managedSecretId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_DeleteManagedSecret
|
|
>('/typedrequest', 'deleteManagedSecret');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
const state = statePartArg.getState();
|
|
return {
|
|
managedSecrets: state.managedSecrets.filter((s) => s.id !== dataArg.managedSecretId),
|
|
};
|
|
} catch (err) {
|
|
console.error('Failed to delete managed secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const pushManagedSecretAction = managedSecretsStatePart.createAction<{
|
|
managedSecretId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_PushManagedSecret
|
|
>('/typedrequest', 'pushManagedSecret');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetManagedSecrets
|
|
>('/typedrequest', 'getManagedSecrets');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { managedSecrets: listResp.managedSecrets };
|
|
} catch (err) {
|
|
console.error('Failed to push managed secret:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const pushAllManagedSecretsAction = managedSecretsStatePart.createAction(
|
|
async (statePartArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_PushAllManagedSecrets
|
|
>('/typedrequest', 'pushAllManagedSecrets');
|
|
await typedRequest.fire({ identity: context.identity! });
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetManagedSecrets
|
|
>('/typedrequest', 'getManagedSecrets');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { managedSecrets: listResp.managedSecrets };
|
|
} catch (err) {
|
|
console.error('Failed to push all managed secrets:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
},
|
|
);
|
|
|
|
// ============================================================================
|
|
// Sync State
|
|
// ============================================================================
|
|
|
|
export interface ISyncState {
|
|
configs: interfaces.data.ISyncConfig[];
|
|
repoStatuses: interfaces.data.ISyncRepoStatus[];
|
|
}
|
|
|
|
export const syncStatePart = await appState.getStatePart<ISyncState>(
|
|
'sync',
|
|
{ configs: [], repoStatuses: [] },
|
|
'soft',
|
|
);
|
|
|
|
export const fetchSyncConfigsAction = syncStatePart.createAction(async (statePartArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSyncConfigs
|
|
>('/typedrequest', 'getSyncConfigs');
|
|
const response = await typedRequest.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), configs: response.configs };
|
|
} catch (err) {
|
|
console.error('Failed to fetch sync configs:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const createSyncConfigAction = syncStatePart.createAction<{
|
|
name: string;
|
|
sourceConnectionId: string;
|
|
targetConnectionId: string;
|
|
targetGroupOffset?: string;
|
|
intervalMinutes?: number;
|
|
enforceDelete?: boolean;
|
|
enforceGroupDelete?: boolean;
|
|
addMirrorHint?: boolean;
|
|
useGroupAvatarsForProjects?: boolean;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_CreateSyncConfig
|
|
>('/typedrequest', 'createSyncConfig');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
// Re-fetch
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSyncConfigs
|
|
>('/typedrequest', 'getSyncConfigs');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), configs: listResp.configs };
|
|
} catch (err) {
|
|
console.error('Failed to create sync config:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const updateSyncConfigAction = syncStatePart.createAction<{
|
|
syncConfigId: string;
|
|
name?: string;
|
|
targetGroupOffset?: string;
|
|
intervalMinutes?: number;
|
|
enforceDelete?: boolean;
|
|
enforceGroupDelete?: boolean;
|
|
addMirrorHint?: boolean;
|
|
useGroupAvatarsForProjects?: boolean;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_UpdateSyncConfig
|
|
>('/typedrequest', 'updateSyncConfig');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSyncConfigs
|
|
>('/typedrequest', 'getSyncConfigs');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), configs: listResp.configs };
|
|
} catch (err) {
|
|
console.error('Failed to update sync config:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const deleteSyncConfigAction = syncStatePart.createAction<{
|
|
syncConfigId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_DeleteSyncConfig
|
|
>('/typedrequest', 'deleteSyncConfig');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
const state = statePartArg.getState();
|
|
return { ...state, configs: state.configs.filter((c) => c.id !== dataArg.syncConfigId) };
|
|
} catch (err) {
|
|
console.error('Failed to delete sync config:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const pauseSyncConfigAction = syncStatePart.createAction<{
|
|
syncConfigId: string;
|
|
paused: boolean;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_PauseSyncConfig
|
|
>('/typedrequest', 'pauseSyncConfig');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSyncConfigs
|
|
>('/typedrequest', 'getSyncConfigs');
|
|
const listResp = await listReq.fire({ identity: context.identity! });
|
|
return { ...statePartArg.getState(), configs: listResp.configs };
|
|
} catch (err) {
|
|
console.error('Failed to pause/resume sync config:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const triggerSyncAction = syncStatePart.createAction<{
|
|
syncConfigId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_TriggerSync
|
|
>('/typedrequest', 'triggerSync');
|
|
await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
return statePartArg.getState();
|
|
} catch (err) {
|
|
console.error('Failed to trigger sync:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
export const fetchSyncRepoStatusesAction = syncStatePart.createAction<{
|
|
syncConfigId: string;
|
|
}>(async (statePartArg, dataArg) => {
|
|
const context = getActionContext();
|
|
try {
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSyncRepoStatuses
|
|
>('/typedrequest', 'getSyncRepoStatuses');
|
|
const response = await typedRequest.fire({ identity: context.identity!, ...dataArg });
|
|
return { ...statePartArg.getState(), repoStatuses: response.statuses };
|
|
} catch (err) {
|
|
console.error('Failed to fetch sync repo statuses:', err);
|
|
return statePartArg.getState();
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// Sync Log — TypedSocket client for server-push entries
|
|
// ============================================================================
|
|
|
|
export async function fetchSyncLogs(limit = 200): Promise<interfaces.data.ISyncLogEntry[]> {
|
|
const identity = loginStatePart.getState().identity;
|
|
if (!identity) throw new Error('Not logged in');
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_GetSyncLogs
|
|
>('/typedrequest', 'getSyncLogs');
|
|
const response = await typedRequest.fire({ identity, limit });
|
|
return response.logs;
|
|
}
|
|
|
|
let syncLogSocketInitialized = false;
|
|
|
|
/**
|
|
* Create a TypedSocket client that handles server-push sync log entries.
|
|
* Dispatches 'gitops-sync-log-entry' custom events on document.
|
|
* Call once after login.
|
|
*/
|
|
export async function initSyncLogSocket(): Promise<void> {
|
|
if (syncLogSocketInitialized) return;
|
|
syncLogSocketInitialized = true;
|
|
|
|
try {
|
|
const typedrouter = new plugins.domtools.plugins.typedrequest.TypedRouter();
|
|
|
|
typedrouter.addTypedHandler(
|
|
new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushSyncLog>(
|
|
'pushSyncLog',
|
|
async (dataArg) => {
|
|
document.dispatchEvent(
|
|
new CustomEvent('gitops-sync-log-entry', { detail: dataArg.entry }),
|
|
);
|
|
return {};
|
|
},
|
|
),
|
|
);
|
|
|
|
const typedsocketClient = await plugins.typedsocket.TypedSocket.createClient(
|
|
typedrouter,
|
|
plugins.typedsocket.TypedSocket.useWindowLocationOriginUrl(),
|
|
{ autoReconnect: true },
|
|
);
|
|
await typedsocketClient.setTag('syncLogClient', {});
|
|
} catch (err) {
|
|
console.error('Failed to init sync log TypedSocket client:', err);
|
|
syncLogSocketInitialized = false;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Preview Helper
|
|
// ============================================================================
|
|
|
|
export async function previewSync(syncConfigId: string): Promise<{
|
|
mappings: Array<{ sourceFullPath: string; targetFullPath: string }>;
|
|
deletions: string[];
|
|
groupDeletions: string[];
|
|
}> {
|
|
const identity = loginStatePart.getState().identity;
|
|
if (!identity) throw new Error('Not logged in');
|
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
interfaces.requests.IReq_PreviewSync
|
|
>('/typedrequest', 'previewSync');
|
|
const response = await typedRequest.fire({ identity, syncConfigId });
|
|
return { mappings: response.mappings, deletions: response.deletions, groupDeletions: response.groupDeletions };
|
|
}
|