feat(webhook): add webhook endpoint and client push notifications, auto-refresh UI, and gitea id mapping fixes
This commit is contained in:
@@ -43,6 +43,14 @@ export class GitopsDashboard extends DeesElement {
|
||||
|
||||
private resolvedViewTabs: Array<{ name: string; iconName: string; element: any }> = [];
|
||||
|
||||
// Auto-refresh timer
|
||||
private autoRefreshTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
// WebSocket client
|
||||
private ws: WebSocket | null = null;
|
||||
private wsReconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private wsIntentionalClose = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
document.title = 'GitOps';
|
||||
@@ -53,7 +61,11 @@ export class GitopsDashboard extends DeesElement {
|
||||
this.loginState = loginState;
|
||||
if (loginState.isLoggedIn) {
|
||||
appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
|
||||
this.connectWebSocket();
|
||||
} else {
|
||||
this.disconnectWebSocket();
|
||||
}
|
||||
this.manageAutoRefreshTimer();
|
||||
});
|
||||
this.rxSubscriptions.push(loginSubscription);
|
||||
|
||||
@@ -62,6 +74,7 @@ export class GitopsDashboard extends DeesElement {
|
||||
.subscribe((uiState) => {
|
||||
this.uiState = uiState;
|
||||
this.syncAppdashView(uiState.activeView);
|
||||
this.manageAutoRefreshTimer();
|
||||
});
|
||||
this.rxSubscriptions.push(uiSubscription);
|
||||
}
|
||||
@@ -78,6 +91,36 @@ export class GitopsDashboard extends DeesElement {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
.auto-refresh-toggle {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 1000;
|
||||
background: rgba(30, 30, 50, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
color: #ccc;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
backdrop-filter: blur(8px);
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.auto-refresh-toggle:hover {
|
||||
background: rgba(40, 40, 70, 0.95);
|
||||
}
|
||||
.auto-refresh-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #666;
|
||||
}
|
||||
.auto-refresh-dot.active {
|
||||
background: #00ff88;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -92,6 +135,15 @@ export class GitopsDashboard extends DeesElement {
|
||||
</dees-simple-appdash>
|
||||
</dees-simple-login>
|
||||
</div>
|
||||
${this.loginState.isLoggedIn ? html`
|
||||
<div
|
||||
class="auto-refresh-toggle"
|
||||
@click=${() => appstate.uiStatePart.dispatchAction(appstate.toggleAutoRefreshAction, null)}
|
||||
>
|
||||
<span class="auto-refresh-dot ${this.uiState.autoRefresh ? 'active' : ''}"></span>
|
||||
Auto-Refresh: ${this.uiState.autoRefresh ? 'ON' : 'OFF'}
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -160,6 +212,93 @@ export class GitopsDashboard extends DeesElement {
|
||||
}
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.clearAutoRefreshTimer();
|
||||
this.disconnectWebSocket();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Auto-refresh timer management
|
||||
// ============================================================================
|
||||
|
||||
private manageAutoRefreshTimer(): void {
|
||||
this.clearAutoRefreshTimer();
|
||||
const { autoRefresh, refreshInterval } = this.uiState;
|
||||
if (autoRefresh && this.loginState.isLoggedIn) {
|
||||
this.autoRefreshTimer = setInterval(() => {
|
||||
document.dispatchEvent(new CustomEvent('gitops-auto-refresh'));
|
||||
}, refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private clearAutoRefreshTimer(): void {
|
||||
if (this.autoRefreshTimer) {
|
||||
clearInterval(this.autoRefreshTimer);
|
||||
this.autoRefreshTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// WebSocket client for webhook push notifications
|
||||
// ============================================================================
|
||||
|
||||
private connectWebSocket(): void {
|
||||
if (this.ws) return;
|
||||
|
||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${location.host}`;
|
||||
|
||||
try {
|
||||
this.wsIntentionalClose = false;
|
||||
this.ws = new WebSocket(wsUrl);
|
||||
|
||||
this.ws.addEventListener('message', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
// TypedSocket wraps messages; look for webhookNotification method
|
||||
if (data?.method === 'webhookNotification' || data?.type === 'webhookEvent') {
|
||||
console.log('Webhook event received:', data);
|
||||
document.dispatchEvent(new CustomEvent('gitops-auto-refresh'));
|
||||
}
|
||||
} catch {
|
||||
// Not JSON, ignore
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.addEventListener('close', () => {
|
||||
this.ws = null;
|
||||
if (!this.wsIntentionalClose && this.loginState.isLoggedIn) {
|
||||
this.wsReconnectTimer = setTimeout(() => {
|
||||
this.connectWebSocket();
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.addEventListener('error', () => {
|
||||
// Will trigger close event
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('WebSocket connection failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
private disconnectWebSocket(): void {
|
||||
this.wsIntentionalClose = true;
|
||||
if (this.wsReconnectTimer) {
|
||||
clearTimeout(this.wsReconnectTimer);
|
||||
this.wsReconnectTimer = null;
|
||||
}
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Login
|
||||
// ============================================================================
|
||||
|
||||
private async login(username: string, password: string) {
|
||||
const domtools = await this.domtoolsPromise;
|
||||
const simpleLogin = this.shadowRoot!.querySelector('dees-simple-login') as any;
|
||||
|
||||
@@ -38,6 +38,8 @@ export class GitopsViewBuildlog extends DeesElement {
|
||||
@state()
|
||||
accessor selectedJobId: string = '';
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const connSub = appstate.connectionsStatePart
|
||||
@@ -49,6 +51,18 @@ export class GitopsViewBuildlog extends DeesElement {
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.dataState = s; });
|
||||
this.rxSubscriptions.push(dataSub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
this.fetchLog();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
@@ -19,12 +19,26 @@ export class GitopsViewConnections extends DeesElement {
|
||||
activeConnectionId: null,
|
||||
};
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const sub = appstate.connectionsStatePart
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.connectionsState = s; });
|
||||
this.rxSubscriptions.push(sub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
@@ -32,6 +32,8 @@ export class GitopsViewGroups extends DeesElement {
|
||||
@state()
|
||||
accessor selectedConnectionId: string = '';
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const connSub = appstate.connectionsStatePart
|
||||
@@ -43,6 +45,18 @@ export class GitopsViewGroups extends DeesElement {
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.dataState = s; });
|
||||
this.rxSubscriptions.push(dataSub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
this.loadGroups();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
@@ -30,6 +30,8 @@ export class GitopsViewOverview extends DeesElement {
|
||||
currentJobLog: '',
|
||||
};
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const connSub = appstate.connectionsStatePart
|
||||
@@ -41,6 +43,18 @@ export class GitopsViewOverview extends DeesElement {
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.dataState = s; });
|
||||
this.rxSubscriptions.push(dataSub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
@@ -35,6 +35,8 @@ export class GitopsViewPipelines extends DeesElement {
|
||||
@state()
|
||||
accessor selectedProjectId: string = '';
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const connSub = appstate.connectionsStatePart
|
||||
@@ -46,6 +48,18 @@ export class GitopsViewPipelines extends DeesElement {
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.dataState = s; });
|
||||
this.rxSubscriptions.push(dataSub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
this.loadPipelines();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
@@ -32,6 +32,8 @@ export class GitopsViewProjects extends DeesElement {
|
||||
@state()
|
||||
accessor selectedConnectionId: string = '';
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const connSub = appstate.connectionsStatePart
|
||||
@@ -43,6 +45,18 @@ export class GitopsViewProjects extends DeesElement {
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.dataState = s; });
|
||||
this.rxSubscriptions.push(dataSub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
this.loadProjects();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
@@ -38,6 +38,8 @@ export class GitopsViewSecrets extends DeesElement {
|
||||
@state()
|
||||
accessor selectedScopeId: string = '';
|
||||
|
||||
private _autoRefreshHandler: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const connSub = appstate.connectionsStatePart
|
||||
@@ -49,6 +51,18 @@ export class GitopsViewSecrets extends DeesElement {
|
||||
.select((s) => s)
|
||||
.subscribe((s) => { this.dataState = s; });
|
||||
this.rxSubscriptions.push(dataSub);
|
||||
|
||||
this._autoRefreshHandler = () => this.handleAutoRefresh();
|
||||
document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
public override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
|
||||
}
|
||||
|
||||
private handleAutoRefresh(): void {
|
||||
this.loadSecrets();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
|
||||
Reference in New Issue
Block a user