@serve.zone/gitops 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.smartconfig.json +114 -0
  2. package/binary/gitops.ts +4 -0
  3. package/changelog.md +185 -0
  4. package/cli.child.js +4 -0
  5. package/cli.js +4 -0
  6. package/cli.ts.js +5 -0
  7. package/deno.json +10 -0
  8. package/dist_serve/bundle.js +36362 -0
  9. package/dist_serve/index.html +33 -0
  10. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  11. package/dist_ts/00_commitinfo_data.js +9 -0
  12. package/dist_ts/cache/classes.cache.cleaner.d.ts +23 -0
  13. package/dist_ts/cache/classes.cache.cleaner.js +61 -0
  14. package/dist_ts/cache/classes.cached.document.d.ts +30 -0
  15. package/dist_ts/cache/classes.cached.document.js +101 -0
  16. package/dist_ts/cache/classes.cachedb.d.ts +22 -0
  17. package/dist_ts/cache/classes.cachedb.js +58 -0
  18. package/dist_ts/cache/classes.secrets.scan.service.d.ts +51 -0
  19. package/dist_ts/cache/classes.secrets.scan.service.js +237 -0
  20. package/dist_ts/cache/documents/classes.cached.project.d.ts +13 -0
  21. package/dist_ts/cache/documents/classes.cached.project.js +101 -0
  22. package/dist_ts/cache/documents/classes.cached.secret.d.ts +24 -0
  23. package/dist_ts/cache/documents/classes.cached.secret.js +158 -0
  24. package/dist_ts/cache/documents/index.d.ts +2 -0
  25. package/dist_ts/cache/documents/index.js +3 -0
  26. package/dist_ts/cache/index.d.ts +7 -0
  27. package/dist_ts/cache/index.js +6 -0
  28. package/dist_ts/classes/actionlog.d.ts +19 -0
  29. package/dist_ts/classes/actionlog.js +44 -0
  30. package/dist_ts/classes/connectionmanager.d.ts +57 -0
  31. package/dist_ts/classes/connectionmanager.js +247 -0
  32. package/dist_ts/classes/gitopsapp.d.ts +30 -0
  33. package/dist_ts/classes/gitopsapp.js +101 -0
  34. package/dist_ts/classes/jobmanager.d.ts +47 -0
  35. package/dist_ts/classes/jobmanager.js +301 -0
  36. package/dist_ts/classes/jobrunners/autobookstackdocs.runner.d.ts +29 -0
  37. package/dist_ts/classes/jobrunners/autobookstackdocs.runner.js +361 -0
  38. package/dist_ts/classes/jobrunners/base.jobrunner.d.ts +14 -0
  39. package/dist_ts/classes/jobrunners/base.jobrunner.js +3 -0
  40. package/dist_ts/classes/jobrunners/index.d.ts +5 -0
  41. package/dist_ts/classes/jobrunners/index.js +14 -0
  42. package/dist_ts/classes/managedsecrets.manager.d.ts +47 -0
  43. package/dist_ts/classes/managedsecrets.manager.js +247 -0
  44. package/dist_ts/classes/syncmanager.d.ts +189 -0
  45. package/dist_ts/classes/syncmanager.js +1787 -0
  46. package/dist_ts/index.d.ts +6 -0
  47. package/dist_ts/index.js +32 -0
  48. package/dist_ts/logging.d.ts +49 -0
  49. package/dist_ts/logging.js +134 -0
  50. package/dist_ts/opsserver/classes.opsserver.d.ts +25 -0
  51. package/dist_ts/opsserver/classes.opsserver.js +70 -0
  52. package/dist_ts/opsserver/handlers/actionlog.handler.d.ts +9 -0
  53. package/dist_ts/opsserver/handlers/actionlog.handler.js +24 -0
  54. package/dist_ts/opsserver/handlers/actions.handler.d.ts +9 -0
  55. package/dist_ts/opsserver/handlers/actions.handler.js +38 -0
  56. package/dist_ts/opsserver/handlers/admin.handler.d.ts +19 -0
  57. package/dist_ts/opsserver/handlers/admin.handler.js +96 -0
  58. package/dist_ts/opsserver/handlers/connections.handler.d.ts +10 -0
  59. package/dist_ts/opsserver/handlers/connections.handler.js +109 -0
  60. package/dist_ts/opsserver/handlers/groups.handler.d.ts +9 -0
  61. package/dist_ts/opsserver/handlers/groups.handler.js +24 -0
  62. package/dist_ts/opsserver/handlers/index.d.ts +13 -0
  63. package/dist_ts/opsserver/handlers/index.js +14 -0
  64. package/dist_ts/opsserver/handlers/jobs.handler.d.ts +16 -0
  65. package/dist_ts/opsserver/handlers/jobs.handler.js +146 -0
  66. package/dist_ts/opsserver/handlers/logs.handler.d.ts +9 -0
  67. package/dist_ts/opsserver/handlers/logs.handler.js +21 -0
  68. package/dist_ts/opsserver/handlers/managedsecrets.handler.d.ts +11 -0
  69. package/dist_ts/opsserver/handlers/managedsecrets.handler.js +110 -0
  70. package/dist_ts/opsserver/handlers/pipelines.handler.d.ts +31 -0
  71. package/dist_ts/opsserver/handlers/pipelines.handler.js +204 -0
  72. package/dist_ts/opsserver/handlers/projects.handler.d.ts +9 -0
  73. package/dist_ts/opsserver/handlers/projects.handler.js +24 -0
  74. package/dist_ts/opsserver/handlers/secrets.handler.d.ts +10 -0
  75. package/dist_ts/opsserver/handlers/secrets.handler.js +171 -0
  76. package/dist_ts/opsserver/handlers/sync.handler.d.ts +16 -0
  77. package/dist_ts/opsserver/handlers/sync.handler.js +166 -0
  78. package/dist_ts/opsserver/handlers/webhook.handler.d.ts +7 -0
  79. package/dist_ts/opsserver/handlers/webhook.handler.js +55 -0
  80. package/dist_ts/opsserver/helpers/guards.d.ts +5 -0
  81. package/dist_ts/opsserver/helpers/guards.js +12 -0
  82. package/dist_ts/opsserver/index.d.ts +1 -0
  83. package/dist_ts/opsserver/index.js +2 -0
  84. package/dist_ts/paths.d.ts +9 -0
  85. package/dist_ts/paths.js +13 -0
  86. package/dist_ts/plugins.d.ts +25 -0
  87. package/dist_ts/plugins.js +32 -0
  88. package/dist_ts/providers/classes.baseprovider.d.ts +51 -0
  89. package/dist_ts/providers/classes.baseprovider.js +17 -0
  90. package/dist_ts/providers/classes.giteaprovider.d.ts +40 -0
  91. package/dist_ts/providers/classes.giteaprovider.js +224 -0
  92. package/dist_ts/providers/classes.gitlabprovider.d.ts +39 -0
  93. package/dist_ts/providers/classes.gitlabprovider.js +207 -0
  94. package/dist_ts/providers/index.d.ts +3 -0
  95. package/dist_ts/providers/index.js +4 -0
  96. package/dist_ts/storage/classes.storagemanager.d.ts +33 -0
  97. package/dist_ts/storage/classes.storagemanager.js +135 -0
  98. package/dist_ts/storage/index.d.ts +2 -0
  99. package/dist_ts/storage/index.js +2 -0
  100. package/dist_ts/timers.d.ts +4 -0
  101. package/dist_ts/timers.js +24 -0
  102. package/dist_ts_bundled/bundle.d.ts +4 -0
  103. package/dist_ts_bundled/bundle.js +12 -0
  104. package/dist_ts_interfaces/data/actionlog.d.ts +12 -0
  105. package/dist_ts_interfaces/data/actionlog.js +2 -0
  106. package/dist_ts_interfaces/data/branch.d.ts +8 -0
  107. package/dist_ts_interfaces/data/branch.js +2 -0
  108. package/dist_ts_interfaces/data/connection.d.ts +12 -0
  109. package/dist_ts_interfaces/data/connection.js +2 -0
  110. package/dist_ts_interfaces/data/group.d.ts +10 -0
  111. package/dist_ts_interfaces/data/group.js +2 -0
  112. package/dist_ts_interfaces/data/identity.d.ts +7 -0
  113. package/dist_ts_interfaces/data/identity.js +2 -0
  114. package/dist_ts_interfaces/data/index.d.ts +11 -0
  115. package/dist_ts_interfaces/data/index.js +12 -0
  116. package/dist_ts_interfaces/data/job.d.ts +37 -0
  117. package/dist_ts_interfaces/data/job.js +2 -0
  118. package/dist_ts_interfaces/data/managedsecret.d.ts +37 -0
  119. package/dist_ts_interfaces/data/managedsecret.js +2 -0
  120. package/dist_ts_interfaces/data/pipeline.d.ts +22 -0
  121. package/dist_ts_interfaces/data/pipeline.js +2 -0
  122. package/dist_ts_interfaces/data/project.d.ts +12 -0
  123. package/dist_ts_interfaces/data/project.js +2 -0
  124. package/dist_ts_interfaces/data/secret.d.ts +11 -0
  125. package/dist_ts_interfaces/data/secret.js +2 -0
  126. package/dist_ts_interfaces/data/sync.d.ts +34 -0
  127. package/dist_ts_interfaces/data/sync.js +2 -0
  128. package/dist_ts_interfaces/index.d.ts +5 -0
  129. package/dist_ts_interfaces/index.js +8 -0
  130. package/dist_ts_interfaces/plugins.d.ts +2 -0
  131. package/dist_ts_interfaces/plugins.js +4 -0
  132. package/dist_ts_interfaces/requests/actionlog.d.ts +15 -0
  133. package/dist_ts_interfaces/requests/actionlog.js +3 -0
  134. package/dist_ts_interfaces/requests/actions.d.ts +31 -0
  135. package/dist_ts_interfaces/requests/actions.js +3 -0
  136. package/dist_ts_interfaces/requests/admin.d.ts +31 -0
  137. package/dist_ts_interfaces/requests/admin.js +3 -0
  138. package/dist_ts_interfaces/requests/connections.d.ts +71 -0
  139. package/dist_ts_interfaces/requests/connections.js +3 -0
  140. package/dist_ts_interfaces/requests/groups.d.ts +14 -0
  141. package/dist_ts_interfaces/requests/groups.js +3 -0
  142. package/dist_ts_interfaces/requests/index.d.ts +13 -0
  143. package/dist_ts_interfaces/requests/index.js +14 -0
  144. package/dist_ts_interfaces/requests/jobs.d.ts +86 -0
  145. package/dist_ts_interfaces/requests/jobs.js +3 -0
  146. package/dist_ts_interfaces/requests/logs.d.ts +14 -0
  147. package/dist_ts_interfaces/requests/logs.js +3 -0
  148. package/dist_ts_interfaces/requests/managedsecrets.d.ts +84 -0
  149. package/dist_ts_interfaces/requests/managedsecrets.js +3 -0
  150. package/dist_ts_interfaces/requests/pipelines.d.ts +55 -0
  151. package/dist_ts_interfaces/requests/pipelines.js +3 -0
  152. package/dist_ts_interfaces/requests/projects.d.ts +14 -0
  153. package/dist_ts_interfaces/requests/projects.js +3 -0
  154. package/dist_ts_interfaces/requests/secrets.d.ts +72 -0
  155. package/dist_ts_interfaces/requests/secrets.js +3 -0
  156. package/dist_ts_interfaces/requests/sync.d.ts +120 -0
  157. package/dist_ts_interfaces/requests/sync.js +3 -0
  158. package/dist_ts_interfaces/requests/webhook.d.ts +13 -0
  159. package/dist_ts_interfaces/requests/webhook.js +3 -0
  160. package/license +21 -0
  161. package/package.json +81 -0
  162. package/readme.md +177 -0
  163. package/readme.todo.md +3 -0
  164. package/ts/00_commitinfo_data.ts +8 -0
  165. package/ts/cache/classes.cache.cleaner.ts +69 -0
  166. package/ts/cache/classes.cached.document.ts +57 -0
  167. package/ts/cache/classes.cachedb.ts +72 -0
  168. package/ts/cache/classes.secrets.scan.service.ts +267 -0
  169. package/ts/cache/documents/classes.cached.project.ts +32 -0
  170. package/ts/cache/documents/classes.cached.secret.ts +81 -0
  171. package/ts/cache/documents/index.ts +2 -0
  172. package/ts/cache/index.ts +7 -0
  173. package/ts/classes/actionlog.ts +57 -0
  174. package/ts/classes/connectionmanager.ts +263 -0
  175. package/ts/classes/gitopsapp.ts +128 -0
  176. package/ts/classes/jobmanager.ts +337 -0
  177. package/ts/classes/jobrunners/autobookstackdocs.runner.ts +435 -0
  178. package/ts/classes/jobrunners/base.jobrunner.ts +16 -0
  179. package/ts/classes/jobrunners/index.ts +17 -0
  180. package/ts/classes/managedsecrets.manager.ts +322 -0
  181. package/ts/classes/syncmanager.ts +2117 -0
  182. package/ts/index.ts +37 -0
  183. package/ts/logging.ts +162 -0
  184. package/ts/opsserver/classes.opsserver.ts +86 -0
  185. package/ts/opsserver/handlers/actionlog.handler.ts +30 -0
  186. package/ts/opsserver/handlers/actions.handler.ts +50 -0
  187. package/ts/opsserver/handlers/admin.handler.ts +122 -0
  188. package/ts/opsserver/handlers/connections.handler.ts +162 -0
  189. package/ts/opsserver/handlers/groups.handler.ts +32 -0
  190. package/ts/opsserver/handlers/index.ts +13 -0
  191. package/ts/opsserver/handlers/jobs.handler.ts +189 -0
  192. package/ts/opsserver/handlers/logs.handler.ts +29 -0
  193. package/ts/opsserver/handlers/managedsecrets.handler.ts +158 -0
  194. package/ts/opsserver/handlers/pipelines.handler.ts +281 -0
  195. package/ts/opsserver/handlers/projects.handler.ts +32 -0
  196. package/ts/opsserver/handlers/secrets.handler.ts +224 -0
  197. package/ts/opsserver/handlers/sync.handler.ts +224 -0
  198. package/ts/opsserver/handlers/webhook.handler.ts +62 -0
  199. package/ts/opsserver/helpers/guards.ts +16 -0
  200. package/ts/opsserver/index.ts +1 -0
  201. package/ts/paths.ts +19 -0
  202. package/ts/plugins.ts +38 -0
  203. package/ts/providers/classes.baseprovider.ts +99 -0
  204. package/ts/providers/classes.giteaprovider.ts +279 -0
  205. package/ts/providers/classes.gitlabprovider.ts +265 -0
  206. package/ts/providers/index.ts +3 -0
  207. package/ts/storage/classes.storagemanager.ts +144 -0
  208. package/ts/storage/index.ts +2 -0
  209. package/ts/timers.ts +34 -0
  210. package/ts_interfaces/data/actionlog.ts +13 -0
  211. package/ts_interfaces/data/branch.ts +9 -0
  212. package/ts_interfaces/data/connection.ts +13 -0
  213. package/ts_interfaces/data/group.ts +10 -0
  214. package/ts_interfaces/data/identity.ts +7 -0
  215. package/ts_interfaces/data/index.ts +11 -0
  216. package/ts_interfaces/data/job.ts +42 -0
  217. package/ts_interfaces/data/managedsecret.ts +41 -0
  218. package/ts_interfaces/data/pipeline.ts +32 -0
  219. package/ts_interfaces/data/project.ts +12 -0
  220. package/ts_interfaces/data/secret.ts +11 -0
  221. package/ts_interfaces/data/sync.ts +37 -0
  222. package/ts_interfaces/index.ts +9 -0
  223. package/ts_interfaces/plugins.ts +6 -0
  224. package/ts_interfaces/requests/actionlog.ts +19 -0
  225. package/ts_interfaces/requests/actions.ts +39 -0
  226. package/ts_interfaces/requests/admin.ts +43 -0
  227. package/ts_interfaces/requests/connections.ts +95 -0
  228. package/ts_interfaces/requests/groups.ts +18 -0
  229. package/ts_interfaces/requests/index.ts +13 -0
  230. package/ts_interfaces/requests/jobs.ts +118 -0
  231. package/ts_interfaces/requests/logs.ts +18 -0
  232. package/ts_interfaces/requests/managedsecrets.ts +112 -0
  233. package/ts_interfaces/requests/pipelines.ts +71 -0
  234. package/ts_interfaces/requests/projects.ts +18 -0
  235. package/ts_interfaces/requests/secrets.ts +92 -0
  236. package/ts_interfaces/requests/sync.ts +157 -0
  237. package/ts_interfaces/requests/webhook.ts +18 -0
  238. package/ts_web/00_commitinfo_data.ts +8 -0
  239. package/ts_web/appstate.ts +1251 -0
  240. package/ts_web/elements/gitops-dashboard.ts +350 -0
  241. package/ts_web/elements/index.ts +10 -0
  242. package/ts_web/elements/shared/css.ts +29 -0
  243. package/ts_web/elements/shared/index.ts +1 -0
  244. package/ts_web/elements/views/actionlog/index.ts +101 -0
  245. package/ts_web/elements/views/actions/index.ts +209 -0
  246. package/ts_web/elements/views/buildlog/index.ts +196 -0
  247. package/ts_web/elements/views/connections/index.ts +260 -0
  248. package/ts_web/elements/views/groups/index.ts +134 -0
  249. package/ts_web/elements/views/jobs/index.ts +424 -0
  250. package/ts_web/elements/views/managedsecrets/index.ts +502 -0
  251. package/ts_web/elements/views/overview/index.ts +86 -0
  252. package/ts_web/elements/views/pipelines/index.ts +561 -0
  253. package/ts_web/elements/views/projects/index.ts +149 -0
  254. package/ts_web/elements/views/secrets/index.ts +310 -0
  255. package/ts_web/elements/views/sync/index.ts +512 -0
  256. package/ts_web/index.ts +7 -0
  257. package/ts_web/plugins.ts +15 -0
  258. package/tsconfig.json +15 -0
@@ -0,0 +1,502 @@
1
+ import * as plugins from '../../../plugins.js';
2
+ import * as appstate from '../../../appstate.js';
3
+ import { viewHostCss } from '../../shared/index.js';
4
+ import {
5
+ DeesElement,
6
+ customElement,
7
+ html,
8
+ state,
9
+ css,
10
+ cssManager,
11
+ type TemplateResult,
12
+ } from '@design.estate/dees-element';
13
+
14
+ @customElement('gitops-view-managedsecrets')
15
+ export class GitopsViewManagedSecrets extends DeesElement {
16
+ @state()
17
+ accessor managedSecretsState: appstate.IManagedSecretsState = { managedSecrets: [] };
18
+
19
+ @state()
20
+ accessor connectionsState: appstate.IConnectionsState = {
21
+ connections: [],
22
+ activeConnectionId: null,
23
+ };
24
+
25
+ @state()
26
+ accessor dataState: appstate.IDataState = {
27
+ projects: [],
28
+ groups: [],
29
+ secrets: [],
30
+ pipelines: [],
31
+ pipelineJobs: [],
32
+ currentJobLog: '',
33
+ };
34
+
35
+ private _autoRefreshHandler: () => void;
36
+
37
+ constructor() {
38
+ super();
39
+ const msSub = appstate.managedSecretsStatePart
40
+ .select((s) => s)
41
+ .subscribe((s) => { this.managedSecretsState = s; });
42
+ this.rxSubscriptions.push(msSub);
43
+
44
+ const connSub = appstate.connectionsStatePart
45
+ .select((s) => s)
46
+ .subscribe((s) => { this.connectionsState = s; });
47
+ this.rxSubscriptions.push(connSub);
48
+
49
+ const dataSub = appstate.dataStatePart
50
+ .select((s) => s)
51
+ .subscribe((s) => { this.dataState = s; });
52
+ this.rxSubscriptions.push(dataSub);
53
+
54
+ this._autoRefreshHandler = () => this.refresh();
55
+ document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
56
+ }
57
+
58
+ public override disconnectedCallback() {
59
+ super.disconnectedCallback();
60
+ document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
61
+ }
62
+
63
+ public static styles = [
64
+ cssManager.defaultStyles,
65
+ viewHostCss,
66
+ css`
67
+ .target-list {
68
+ margin: 8px 0;
69
+ }
70
+ .target-item {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: space-between;
74
+ padding: 6px 10px;
75
+ margin: 4px 0;
76
+ background: rgba(255, 255, 255, 0.05);
77
+ border-radius: 4px;
78
+ color: #ccc;
79
+ font-size: 13px;
80
+ }
81
+ .target-item .remove-btn {
82
+ cursor: pointer;
83
+ color: #e74c3c;
84
+ font-size: 16px;
85
+ padding: 0 4px;
86
+ }
87
+ .status-ok { color: #2ecc71; }
88
+ .status-error { color: #e74c3c; }
89
+ .status-pending { color: #f39c12; }
90
+ `,
91
+ ];
92
+
93
+ public render(): TemplateResult {
94
+ return html`
95
+ <div class="view-title">Managed Secrets</div>
96
+ <div class="view-description">Centrally managed secrets pushed as GITOPS_{key} to configured targets</div>
97
+ <div class="toolbar">
98
+ <dees-button @click=${() => this.addManagedSecret()}>Add Managed Secret</dees-button>
99
+ <dees-button @click=${() => this.pushAll()}>Push All</dees-button>
100
+ <dees-button @click=${() => this.refresh()}>Refresh</dees-button>
101
+ </div>
102
+ <dees-table
103
+ .heading1=${'Managed Secrets'}
104
+ .heading2=${'Define once, push to many targets'}
105
+ .data=${this.managedSecretsState.managedSecrets}
106
+ .displayFunction=${(item: any) => ({
107
+ Key: item.key,
108
+ 'On Target': 'GITOPS_' + item.key,
109
+ Description: item.description || '-',
110
+ Targets: String(item.targets.length),
111
+ Status: this.summarizeStatus(item.targetStatuses),
112
+ 'Last Pushed': item.lastPushedAt ? new Date(item.lastPushedAt).toLocaleString() : 'Never',
113
+ })}
114
+ .dataActions=${[
115
+ {
116
+ name: 'Edit',
117
+ iconName: 'lucide:edit',
118
+ type: ['inRow', 'contextmenu'],
119
+ actionFunc: async ({ item }: any) => { await this.editManagedSecret(item); },
120
+ },
121
+ {
122
+ name: 'Push',
123
+ iconName: 'lucide:upload',
124
+ type: ['inRow', 'contextmenu'],
125
+ actionFunc: async ({ item }: any) => { await this.pushOne(item); },
126
+ },
127
+ {
128
+ name: 'View Targets',
129
+ iconName: 'lucide:list',
130
+ type: ['inRow', 'contextmenu'],
131
+ actionFunc: async ({ item }: any) => { await this.viewTargets(item); },
132
+ },
133
+ {
134
+ name: 'Delete',
135
+ iconName: 'lucide:trash2',
136
+ type: ['inRow', 'contextmenu'],
137
+ actionFunc: async ({ item }: any) => { await this.deleteManagedSecret(item); },
138
+ },
139
+ ]}
140
+ ></dees-table>
141
+ `;
142
+ }
143
+
144
+ async firstUpdated() {
145
+ await appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
146
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.fetchManagedSecretsAction, null);
147
+ }
148
+
149
+ private summarizeStatus(statuses: any[]): string {
150
+ if (!statuses || statuses.length === 0) return 'Not pushed';
151
+ const ok = statuses.filter((s: any) => s.status === 'success').length;
152
+ const err = statuses.filter((s: any) => s.status === 'error').length;
153
+ if (err === 0) return `All OK (${ok})`;
154
+ return `${ok} OK / ${err} Failed`;
155
+ }
156
+
157
+ private async refresh() {
158
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.fetchManagedSecretsAction, null);
159
+ }
160
+
161
+ private async pushAll() {
162
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.pushAllManagedSecretsAction, null);
163
+ }
164
+
165
+ private async pushOne(item: any) {
166
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.pushManagedSecretAction, {
167
+ managedSecretId: item.id,
168
+ });
169
+ }
170
+
171
+ private async deleteManagedSecret(item: any) {
172
+ await plugins.deesCatalog.DeesModal.createAndShow({
173
+ heading: 'Delete Managed Secret',
174
+ content: html`<p style="color: #fff;">Are you sure you want to delete managed secret "${item.key}"?<br>This will also remove GITOPS_${item.key} from all ${item.targets.length} target(s).</p>`,
175
+ menuOptions: [
176
+ { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
177
+ {
178
+ name: 'Delete',
179
+ action: async (modal: any) => {
180
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.deleteManagedSecretAction, {
181
+ managedSecretId: item.id,
182
+ });
183
+ modal.destroy();
184
+ },
185
+ },
186
+ ],
187
+ });
188
+ }
189
+
190
+ private async viewTargets(item: any) {
191
+ const targetRows = (item.targetStatuses && item.targetStatuses.length > 0)
192
+ ? item.targetStatuses
193
+ : item.targets.map((t: any) => ({ ...t, status: 'pending' }));
194
+
195
+ const statusIcon = (status: string) => {
196
+ if (status === 'success') return html`<span class="status-ok">OK</span>`;
197
+ if (status === 'error') return html`<span class="status-error">Error</span>`;
198
+ return html`<span class="status-pending">Pending</span>`;
199
+ };
200
+
201
+ await plugins.deesCatalog.DeesModal.createAndShow({
202
+ heading: `Targets for ${item.key}`,
203
+ content: html`
204
+ <div style="color: #ccc; min-width: 400px;">
205
+ ${targetRows.map((t: any) => html`
206
+ <div style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1);">
207
+ <div>
208
+ <div style="font-weight: bold;">${t.scopeName || t.scopeId}</div>
209
+ <div style="font-size: 12px; opacity: 0.7;">${t.scope} on ${this.getConnectionName(t.connectionId)}</div>
210
+ ${t.error ? html`<div style="font-size: 12px; color: #e74c3c; margin-top: 4px;">${t.error}</div>` : ''}
211
+ </div>
212
+ <div>${statusIcon(t.status)}</div>
213
+ </div>
214
+ `)}
215
+ ${targetRows.length === 0 ? html`<p>No targets configured.</p>` : ''}
216
+ </div>
217
+ `,
218
+ menuOptions: [
219
+ { name: 'Close', action: async (modal: any) => { modal.destroy(); } },
220
+ ],
221
+ });
222
+ }
223
+
224
+ private getConnectionName(connectionId: string): string {
225
+ const conn = this.connectionsState.connections.find((c) => c.id === connectionId);
226
+ return conn ? conn.name : connectionId;
227
+ }
228
+
229
+ private async addManagedSecret() {
230
+ // Load entities for all connections
231
+ const connections = this.connectionsState.connections;
232
+ let targets: any[] = [];
233
+ let selectedConnId = connections.length > 0 ? connections[0].id : '';
234
+ let selectedScope: 'project' | 'group' = 'project';
235
+
236
+ // Pre-load entities for first connection
237
+ if (selectedConnId) {
238
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, {
239
+ connectionId: selectedConnId,
240
+ });
241
+ }
242
+
243
+ const buildTargetListHtml = () => {
244
+ if (targets.length === 0) return html`<p style="color: #888; font-size: 13px;">No targets added yet.</p>`;
245
+ return html`${targets.map((t, i) => html`
246
+ <div class="target-item">
247
+ <span>${t.scopeName} (${t.scope}) on ${this.getConnectionName(t.connectionId)}</span>
248
+ <span class="remove-btn" @click=${() => { targets.splice(i, 1); this.requestUpdate(); }}>x</span>
249
+ </div>
250
+ `)}`;
251
+ };
252
+
253
+ await plugins.deesCatalog.DeesModal.createAndShow({
254
+ heading: 'Add Managed Secret',
255
+ content: html`
256
+ <style>
257
+ .form-row { margin-bottom: 16px; }
258
+ .target-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(255,255,255,0.1); }
259
+ .add-target-row { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
260
+ </style>
261
+ <div class="form-row">
262
+ <dees-input-text .label=${'Key'} .key=${'key'} .description=${'Will be stored as GITOPS_{key} on targets'}></dees-input-text>
263
+ </div>
264
+ <div class="form-row">
265
+ <dees-input-text .label=${'Value'} .key=${'value'} type="password"></dees-input-text>
266
+ </div>
267
+ <div class="form-row">
268
+ <dees-input-text .label=${'Description'} .key=${'description'}></dees-input-text>
269
+ </div>
270
+ <div class="target-section">
271
+ <div style="color: #fff; font-weight: bold; margin-bottom: 8px;">Targets</div>
272
+ <div class="add-target-row">
273
+ <dees-input-dropdown
274
+ .label=${'Connection'}
275
+ .key=${'targetConn'}
276
+ .options=${connections.map((c) => ({ option: c.name, key: c.id }))}
277
+ .selectedOption=${connections.length > 0 ? { option: connections[0].name, key: connections[0].id } : undefined}
278
+ @selectedOption=${async (e: CustomEvent) => {
279
+ selectedConnId = e.detail.key;
280
+ if (selectedScope === 'project') {
281
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, { connectionId: selectedConnId });
282
+ } else {
283
+ await appstate.dataStatePart.dispatchAction(appstate.fetchGroupsAction, { connectionId: selectedConnId });
284
+ }
285
+ }}
286
+ ></dees-input-dropdown>
287
+ <dees-input-dropdown
288
+ .label=${'Scope'}
289
+ .key=${'targetScope'}
290
+ .options=${[{ option: 'Project', key: 'project' }, { option: 'Group', key: 'group' }]}
291
+ .selectedOption=${{ option: 'Project', key: 'project' }}
292
+ @selectedOption=${async (e: CustomEvent) => {
293
+ selectedScope = e.detail.key as 'project' | 'group';
294
+ if (selectedScope === 'project') {
295
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, { connectionId: selectedConnId });
296
+ } else {
297
+ await appstate.dataStatePart.dispatchAction(appstate.fetchGroupsAction, { connectionId: selectedConnId });
298
+ }
299
+ }}
300
+ ></dees-input-dropdown>
301
+ <dees-input-dropdown
302
+ .label=${'Entity'}
303
+ .key=${'targetEntity'}
304
+ .options=${(() => {
305
+ const data = appstate.dataStatePart.getState();
306
+ const items = selectedScope === 'project' ? data.projects : data.groups;
307
+ return items.map((item: any) => ({ option: item.fullPath || item.name, key: item.id }));
308
+ })()}
309
+ ></dees-input-dropdown>
310
+ <dees-button @click=${() => {
311
+ const data = appstate.dataStatePart.getState();
312
+ const items = selectedScope === 'project' ? data.projects : data.groups;
313
+ // Find entity dropdown value
314
+ const modal = document.querySelector('dees-modal');
315
+ if (!modal) return;
316
+ const entityDropdowns = modal.shadowRoot?.querySelectorAll('dees-input-dropdown');
317
+ let entityKey = '';
318
+ let entityName = '';
319
+ if (entityDropdowns) {
320
+ for (const dd of entityDropdowns) {
321
+ if ((dd as any).key === 'targetEntity') {
322
+ const sel = (dd as any).selectedOption;
323
+ if (sel) {
324
+ entityKey = sel.key;
325
+ entityName = sel.option;
326
+ }
327
+ }
328
+ }
329
+ }
330
+ if (!entityKey) return;
331
+ // Avoid duplicates
332
+ const exists = targets.some(
333
+ (t) => t.connectionId === selectedConnId && t.scope === selectedScope && t.scopeId === entityKey,
334
+ );
335
+ if (exists) return;
336
+ targets.push({
337
+ connectionId: selectedConnId,
338
+ scope: selectedScope,
339
+ scopeId: entityKey,
340
+ scopeName: entityName,
341
+ });
342
+ }}>Add Target</dees-button>
343
+ </div>
344
+ <div class="target-list" id="targetList">
345
+ ${buildTargetListHtml()}
346
+ </div>
347
+ </div>
348
+ `,
349
+ menuOptions: [
350
+ { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
351
+ {
352
+ name: 'Create',
353
+ action: async (modal: any) => {
354
+ const inputs = modal.shadowRoot.querySelectorAll('dees-input-text');
355
+ const data: any = {};
356
+ for (const input of inputs) { data[input.key] = input.value || ''; }
357
+ if (!data.key) return;
358
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.createManagedSecretAction, {
359
+ key: data.key,
360
+ value: data.value,
361
+ description: data.description || undefined,
362
+ targets,
363
+ });
364
+ modal.destroy();
365
+ },
366
+ },
367
+ ],
368
+ });
369
+ }
370
+
371
+ private async editManagedSecret(item: any) {
372
+ const connections = this.connectionsState.connections;
373
+ let targets = [...item.targets];
374
+ let selectedConnId = connections.length > 0 ? connections[0].id : '';
375
+ let selectedScope: 'project' | 'group' = 'project';
376
+
377
+ if (selectedConnId) {
378
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, {
379
+ connectionId: selectedConnId,
380
+ });
381
+ }
382
+
383
+ await plugins.deesCatalog.DeesModal.createAndShow({
384
+ heading: `Edit: ${item.key}`,
385
+ content: html`
386
+ <style>
387
+ .form-row { margin-bottom: 16px; }
388
+ .target-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(255,255,255,0.1); }
389
+ .add-target-row { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
390
+ </style>
391
+ <div class="form-row">
392
+ <dees-input-text .label=${'Value (leave empty to keep current)'} .key=${'value'} type="password"></dees-input-text>
393
+ </div>
394
+ <div class="form-row">
395
+ <dees-input-text .label=${'Description'} .key=${'description'} .value=${item.description || ''}></dees-input-text>
396
+ </div>
397
+ <div class="target-section">
398
+ <div style="color: #fff; font-weight: bold; margin-bottom: 8px;">Targets</div>
399
+ <div class="add-target-row">
400
+ <dees-input-dropdown
401
+ .label=${'Connection'}
402
+ .key=${'targetConn'}
403
+ .options=${connections.map((c) => ({ option: c.name, key: c.id }))}
404
+ .selectedOption=${connections.length > 0 ? { option: connections[0].name, key: connections[0].id } : undefined}
405
+ @selectedOption=${async (e: CustomEvent) => {
406
+ selectedConnId = e.detail.key;
407
+ if (selectedScope === 'project') {
408
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, { connectionId: selectedConnId });
409
+ } else {
410
+ await appstate.dataStatePart.dispatchAction(appstate.fetchGroupsAction, { connectionId: selectedConnId });
411
+ }
412
+ }}
413
+ ></dees-input-dropdown>
414
+ <dees-input-dropdown
415
+ .label=${'Scope'}
416
+ .key=${'targetScope'}
417
+ .options=${[{ option: 'Project', key: 'project' }, { option: 'Group', key: 'group' }]}
418
+ .selectedOption=${{ option: 'Project', key: 'project' }}
419
+ @selectedOption=${async (e: CustomEvent) => {
420
+ selectedScope = e.detail.key as 'project' | 'group';
421
+ if (selectedScope === 'project') {
422
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, { connectionId: selectedConnId });
423
+ } else {
424
+ await appstate.dataStatePart.dispatchAction(appstate.fetchGroupsAction, { connectionId: selectedConnId });
425
+ }
426
+ }}
427
+ ></dees-input-dropdown>
428
+ <dees-input-dropdown
429
+ .label=${'Entity'}
430
+ .key=${'targetEntity'}
431
+ .options=${(() => {
432
+ const data = appstate.dataStatePart.getState();
433
+ const items = selectedScope === 'project' ? data.projects : data.groups;
434
+ return items.map((item: any) => ({ option: item.fullPath || item.name, key: item.id }));
435
+ })()}
436
+ ></dees-input-dropdown>
437
+ <dees-button @click=${() => {
438
+ const modal = document.querySelector('dees-modal');
439
+ if (!modal) return;
440
+ const entityDropdowns = modal.shadowRoot?.querySelectorAll('dees-input-dropdown');
441
+ let entityKey = '';
442
+ let entityName = '';
443
+ if (entityDropdowns) {
444
+ for (const dd of entityDropdowns) {
445
+ if ((dd as any).key === 'targetEntity') {
446
+ const sel = (dd as any).selectedOption;
447
+ if (sel) {
448
+ entityKey = sel.key;
449
+ entityName = sel.option;
450
+ }
451
+ }
452
+ }
453
+ }
454
+ if (!entityKey) return;
455
+ const exists = targets.some(
456
+ (t: any) => t.connectionId === selectedConnId && t.scope === selectedScope && t.scopeId === entityKey,
457
+ );
458
+ if (exists) return;
459
+ targets.push({
460
+ connectionId: selectedConnId,
461
+ scope: selectedScope,
462
+ scopeId: entityKey,
463
+ scopeName: entityName,
464
+ });
465
+ }}>Add Target</dees-button>
466
+ </div>
467
+ <div class="target-list">
468
+ ${targets.length === 0
469
+ ? html`<p style="color: #888; font-size: 13px;">No targets.</p>`
470
+ : targets.map((t: any, i: number) => html`
471
+ <div class="target-item">
472
+ <span>${t.scopeName} (${t.scope}) on ${this.getConnectionName(t.connectionId)}</span>
473
+ <span class="remove-btn" @click=${() => { targets.splice(i, 1); }}>x</span>
474
+ </div>
475
+ `)}
476
+ </div>
477
+ </div>
478
+ `,
479
+ menuOptions: [
480
+ { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
481
+ {
482
+ name: 'Update',
483
+ action: async (modal: any) => {
484
+ const inputs = modal.shadowRoot.querySelectorAll('dees-input-text');
485
+ const data: any = {};
486
+ for (const input of inputs) { data[input.key] = input.value || ''; }
487
+ const updatePayload: any = {
488
+ managedSecretId: item.id,
489
+ targets,
490
+ description: data.description || undefined,
491
+ };
492
+ if (data.value) {
493
+ updatePayload.value = data.value;
494
+ }
495
+ await appstate.managedSecretsStatePart.dispatchAction(appstate.updateManagedSecretAction, updatePayload);
496
+ modal.destroy();
497
+ },
498
+ },
499
+ ],
500
+ });
501
+ }
502
+ }
@@ -0,0 +1,86 @@
1
+ import * as plugins from '../../../plugins.js';
2
+ import * as appstate from '../../../appstate.js';
3
+ import { viewHostCss } from '../../shared/index.js';
4
+ import {
5
+ DeesElement,
6
+ customElement,
7
+ html,
8
+ state,
9
+ css,
10
+ cssManager,
11
+ type TemplateResult,
12
+ } from '@design.estate/dees-element';
13
+ import { type IStatsTile } from '@design.estate/dees-catalog';
14
+
15
+ @customElement('gitops-view-overview')
16
+ export class GitopsViewOverview extends DeesElement {
17
+ @state()
18
+ accessor connectionsState: appstate.IConnectionsState = {
19
+ connections: [],
20
+ activeConnectionId: null,
21
+ };
22
+
23
+ @state()
24
+ accessor dataState: appstate.IDataState = {
25
+ projects: [],
26
+ groups: [],
27
+ secrets: [],
28
+ pipelines: [],
29
+ pipelineJobs: [],
30
+ currentJobLog: '',
31
+ };
32
+
33
+ private _autoRefreshHandler: () => void;
34
+
35
+ constructor() {
36
+ super();
37
+ const connSub = appstate.connectionsStatePart
38
+ .select((s) => s)
39
+ .subscribe((s) => { this.connectionsState = s; });
40
+ this.rxSubscriptions.push(connSub);
41
+
42
+ const dataSub = appstate.dataStatePart
43
+ .select((s) => s)
44
+ .subscribe((s) => { this.dataState = s; });
45
+ this.rxSubscriptions.push(dataSub);
46
+
47
+ this._autoRefreshHandler = () => this.handleAutoRefresh();
48
+ document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
49
+ }
50
+
51
+ public override disconnectedCallback() {
52
+ super.disconnectedCallback();
53
+ document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
54
+ }
55
+
56
+ private handleAutoRefresh(): void {
57
+ appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
58
+ }
59
+
60
+ public static styles = [
61
+ cssManager.defaultStyles,
62
+ viewHostCss,
63
+ ];
64
+
65
+ public render(): TemplateResult {
66
+ const connCount = this.connectionsState.connections.length;
67
+ const projCount = this.dataState.projects.length;
68
+ const groupCount = this.dataState.groups.length;
69
+ const pipelineCount = this.dataState.pipelines.length;
70
+ const failedPipelines = this.dataState.pipelines.filter((p) => p.status === 'failed').length;
71
+
72
+ const tiles: IStatsTile[] = [
73
+ { id: 'connections', title: 'Connections', value: connCount, type: 'number', icon: 'lucide:plug', color: '#00acff' },
74
+ { id: 'projects', title: 'Projects', value: projCount, type: 'number', icon: 'lucide:folderGit2', color: '#00acff' },
75
+ { id: 'groups', title: 'Groups', value: groupCount, type: 'number', icon: 'lucide:users', color: '#00acff' },
76
+ { id: 'pipelines', title: 'Pipelines', value: pipelineCount, type: 'number', icon: 'lucide:play', color: '#00acff' },
77
+ { id: 'failed', title: 'Failed Pipelines', value: failedPipelines, type: 'number', icon: 'lucide:triangleAlert', color: failedPipelines > 0 ? '#ff4444' : '#00ff88' },
78
+ ];
79
+
80
+ return html`
81
+ <div class="view-title">Overview</div>
82
+ <div class="view-description">GitOps dashboard - manage your Gitea and GitLab instances</div>
83
+ <dees-statsgrid .tiles=${tiles}></dees-statsgrid>
84
+ `;
85
+ }
86
+ }