@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,561 @@
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
+ type TViewMode = 'current' | 'project' | 'group' | 'error';
15
+ type TSortBy = 'created' | 'duration' | 'status';
16
+ type TTimeRange = '1h' | '6h' | '1d' | '3d' | '7d' | '30d';
17
+
18
+ @customElement('gitops-view-pipelines')
19
+ export class GitopsViewPipelines extends DeesElement {
20
+ @state()
21
+ accessor connectionsState: appstate.IConnectionsState = {
22
+ connections: [],
23
+ activeConnectionId: null,
24
+ };
25
+
26
+ @state()
27
+ accessor dataState: appstate.IDataState = {
28
+ projects: [],
29
+ groups: [],
30
+ secrets: [],
31
+ pipelines: [],
32
+ pipelineJobs: [],
33
+ currentJobLog: '',
34
+ };
35
+
36
+ @state() accessor selectedConnectionId: string = '';
37
+ @state() accessor selectedProjectId: string = '';
38
+ @state() accessor selectedGroupId: string = '';
39
+ @state() accessor viewMode: TViewMode = 'current';
40
+ @state() accessor sortBy: TSortBy = 'created';
41
+ @state() accessor timeRange: TTimeRange = '1d';
42
+ @state() accessor isLoading: boolean = false;
43
+
44
+ private _autoRefreshHandler: () => void;
45
+ private _logPollInterval: ReturnType<typeof setInterval> | null = null;
46
+
47
+ constructor() {
48
+ super();
49
+ const connSub = appstate.connectionsStatePart
50
+ .select((s) => s)
51
+ .subscribe((s) => { this.connectionsState = s; });
52
+ this.rxSubscriptions.push(connSub);
53
+
54
+ const dataSub = appstate.dataStatePart
55
+ .select((s) => s)
56
+ .subscribe((s) => { this.dataState = s; });
57
+ this.rxSubscriptions.push(dataSub);
58
+
59
+ this._autoRefreshHandler = () => this.handleAutoRefresh();
60
+ document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
61
+ }
62
+
63
+ public override disconnectedCallback() {
64
+ super.disconnectedCallback();
65
+ document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
66
+ this.stopLogPolling();
67
+ }
68
+
69
+ private handleAutoRefresh(): void {
70
+ this.loadPipelines();
71
+ }
72
+
73
+ public static styles = [
74
+ cssManager.defaultStyles,
75
+ viewHostCss,
76
+ css`
77
+ .status-badge {
78
+ display: inline-block;
79
+ padding: 2px 8px;
80
+ border-radius: 4px;
81
+ font-size: 12px;
82
+ font-weight: 600;
83
+ text-transform: uppercase;
84
+ }
85
+ .status-success { background: #1a3a1a; color: #00ff88; }
86
+ .status-failed { background: #3a1a1a; color: #ff4444; }
87
+ .status-running { background: #1a2a3a; color: #00acff; }
88
+ .status-pending { background: #3a3a1a; color: #ffaa00; }
89
+ .status-canceled { background: #2a2a2a; color: #999; }
90
+ `,
91
+ ];
92
+
93
+ public render(): TemplateResult {
94
+ const connectionOptions = this.connectionsState.connections.map((c) => ({
95
+ option: `${c.name} (${c.providerType})`,
96
+ key: c.id,
97
+ }));
98
+
99
+ const viewModeOptions = [
100
+ { option: 'Current', key: 'current' },
101
+ { option: 'Project', key: 'project' },
102
+ { option: 'Group', key: 'group' },
103
+ { option: 'Error', key: 'error' },
104
+ ];
105
+
106
+ const timeRangeOptions = [
107
+ { option: '1 hour', key: '1h' },
108
+ { option: '6 hours', key: '6h' },
109
+ { option: '1 day', key: '1d' },
110
+ { option: '3 days', key: '3d' },
111
+ { option: '7 days', key: '7d' },
112
+ { option: '30 days', key: '30d' },
113
+ ];
114
+
115
+ const sortByOptions = [
116
+ { option: 'Created', key: 'created' },
117
+ { option: 'Duration', key: 'duration' },
118
+ { option: 'Status', key: 'status' },
119
+ ];
120
+
121
+ const projectOptions = this.dataState.projects.map((p) => ({
122
+ option: p.fullPath || p.name,
123
+ key: p.id,
124
+ }));
125
+
126
+ const groupOptions = this.dataState.groups.map((g) => ({
127
+ option: g.fullPath || g.name,
128
+ key: g.id,
129
+ }));
130
+
131
+ const showMultiProjectColumns = this.viewMode !== 'project';
132
+
133
+ return html`
134
+ <div class="view-title">Pipelines</div>
135
+ <div class="view-description">View and manage CI/CD pipelines</div>
136
+ <div class="toolbar">
137
+ <dees-input-dropdown
138
+ .label=${'Connection'}
139
+ .options=${connectionOptions}
140
+ .selectedOption=${connectionOptions.find((o) => o.key === this.selectedConnectionId) || connectionOptions[0]}
141
+ @selectedOption=${(e: CustomEvent) => {
142
+ this.selectedConnectionId = e.detail.key;
143
+ this.onConnectionChange();
144
+ }}
145
+ ></dees-input-dropdown>
146
+ <dees-input-dropdown
147
+ .label=${'View'}
148
+ .options=${viewModeOptions}
149
+ .selectedOption=${viewModeOptions.find((o) => o.key === this.viewMode)}
150
+ @selectedOption=${(e: CustomEvent) => {
151
+ this.onViewModeChange(e.detail.key);
152
+ }}
153
+ ></dees-input-dropdown>
154
+ ${this.viewMode === 'project' ? html`
155
+ <dees-input-dropdown
156
+ .label=${'Project'}
157
+ .options=${projectOptions}
158
+ .selectedOption=${projectOptions.find((o) => o.key === this.selectedProjectId) || projectOptions[0]}
159
+ @selectedOption=${(e: CustomEvent) => {
160
+ this.selectedProjectId = e.detail.key;
161
+ this.loadPipelines();
162
+ }}
163
+ ></dees-input-dropdown>
164
+ ` : ''}
165
+ ${this.viewMode === 'group' ? html`
166
+ <dees-input-dropdown
167
+ .label=${'Group'}
168
+ .options=${groupOptions}
169
+ .selectedOption=${groupOptions.find((o) => o.key === this.selectedGroupId) || groupOptions[0]}
170
+ @selectedOption=${(e: CustomEvent) => {
171
+ this.selectedGroupId = e.detail.key;
172
+ this.loadPipelines();
173
+ }}
174
+ ></dees-input-dropdown>
175
+ ` : ''}
176
+ <dees-input-dropdown
177
+ .label=${'Time'}
178
+ .options=${timeRangeOptions}
179
+ .selectedOption=${timeRangeOptions.find((o) => o.key === this.timeRange)}
180
+ @selectedOption=${(e: CustomEvent) => {
181
+ this.timeRange = e.detail.key;
182
+ this.loadPipelines();
183
+ }}
184
+ ></dees-input-dropdown>
185
+ <dees-input-dropdown
186
+ .label=${'Sort'}
187
+ .options=${sortByOptions}
188
+ .selectedOption=${sortByOptions.find((o) => o.key === this.sortBy)}
189
+ @selectedOption=${(e: CustomEvent) => {
190
+ this.sortBy = e.detail.key;
191
+ this.loadPipelines();
192
+ }}
193
+ ></dees-input-dropdown>
194
+ <dees-button @click=${() => this.loadPipelines()}>Refresh</dees-button>
195
+ </div>
196
+ <dees-table
197
+ .heading1=${'CI/CD Pipelines'}
198
+ .heading2=${this.isLoading ? 'Loading...' : `${this.dataState.pipelines.length} pipeline runs`}
199
+ .data=${this.dataState.pipelines}
200
+ .displayFunction=${(item: any) => {
201
+ const row: any = {};
202
+ row['ID'] = item.id;
203
+ row['Status'] = item.status;
204
+ if (showMultiProjectColumns) {
205
+ row['Project'] = item.projectName;
206
+ }
207
+ row['Ref'] = item.ref;
208
+ row['Duration'] = item.duration ? `${Math.round(item.duration)}s` : '-';
209
+ row['Source'] = item.source;
210
+ row['Created'] = item.createdAt ? new Date(item.createdAt).toLocaleString() : '-';
211
+ return row;
212
+ }}
213
+ .dataActions=${[
214
+ {
215
+ name: 'View Logs',
216
+ iconName: 'lucide:terminal',
217
+ type: ['inRow', 'contextmenu'],
218
+ actionFunc: async ({ item }: any) => { await this.openPipelineLogs(item); },
219
+ },
220
+ {
221
+ name: 'View Jobs',
222
+ iconName: 'lucide:list',
223
+ type: ['contextmenu'],
224
+ actionFunc: async ({ item }: any) => { await this.viewJobs(item); },
225
+ },
226
+ {
227
+ name: 'Retry',
228
+ iconName: 'lucide:refreshCw',
229
+ type: ['inRow', 'contextmenu'],
230
+ actionFunc: async ({ item }: any) => {
231
+ await appstate.dataStatePart.dispatchAction(appstate.retryPipelineAction, {
232
+ connectionId: item.connectionId || this.selectedConnectionId,
233
+ projectId: item.projectId,
234
+ pipelineId: item.id,
235
+ });
236
+ await this.loadPipelines();
237
+ },
238
+ },
239
+ {
240
+ name: 'Cancel',
241
+ iconName: 'lucide:xCircle',
242
+ type: ['contextmenu'],
243
+ actionFunc: async ({ item }: any) => {
244
+ await appstate.dataStatePart.dispatchAction(appstate.cancelPipelineAction, {
245
+ connectionId: item.connectionId || this.selectedConnectionId,
246
+ projectId: item.projectId,
247
+ pipelineId: item.id,
248
+ });
249
+ await this.loadPipelines();
250
+ },
251
+ },
252
+ ]}
253
+ ></dees-table>
254
+ `;
255
+ }
256
+
257
+ async firstUpdated() {
258
+ await appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
259
+
260
+ // Check for navigation context from projects view
261
+ const navCtx = appstate.uiStatePart.getState().navigationContext;
262
+ if (navCtx?.connectionId && navCtx?.projectId) {
263
+ this.selectedConnectionId = navCtx.connectionId;
264
+ this.selectedProjectId = navCtx.projectId;
265
+ this.viewMode = 'project';
266
+ appstate.uiStatePart.dispatchAction(appstate.clearNavigationContextAction, null);
267
+ await this.loadProjects();
268
+ await this.loadPipelines();
269
+ return;
270
+ }
271
+
272
+ const conns = appstate.connectionsStatePart.getState().connections;
273
+ if (conns.length > 0 && !this.selectedConnectionId) {
274
+ this.selectedConnectionId = conns[0].id;
275
+ // In 'current' mode, load pipelines immediately
276
+ await this.loadPipelines();
277
+ }
278
+ }
279
+
280
+ // ---------------------------------------------------------------------------
281
+ // Data loading
282
+ // ---------------------------------------------------------------------------
283
+
284
+ private async onConnectionChange() {
285
+ this.selectedProjectId = '';
286
+ this.selectedGroupId = '';
287
+ if (this.viewMode === 'project') {
288
+ await this.loadProjects();
289
+ } else if (this.viewMode === 'group') {
290
+ await this.loadGroups();
291
+ } else {
292
+ await this.loadPipelines();
293
+ }
294
+ }
295
+
296
+ private onViewModeChange(newMode: TViewMode) {
297
+ this.stopLogPolling();
298
+ this.viewMode = newMode;
299
+ this.selectedProjectId = '';
300
+ this.selectedGroupId = '';
301
+
302
+ if (newMode === 'current' || newMode === 'error') {
303
+ this.loadPipelines();
304
+ } else if (newMode === 'project') {
305
+ this.loadProjects();
306
+ } else if (newMode === 'group') {
307
+ this.loadGroups();
308
+ }
309
+ }
310
+
311
+ private async loadProjects() {
312
+ if (!this.selectedConnectionId) return;
313
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, {
314
+ connectionId: this.selectedConnectionId,
315
+ });
316
+ }
317
+
318
+ private async loadGroups() {
319
+ if (!this.selectedConnectionId) return;
320
+ await appstate.dataStatePart.dispatchAction(appstate.fetchGroupsAction, {
321
+ connectionId: this.selectedConnectionId,
322
+ });
323
+ }
324
+
325
+ private async loadPipelines() {
326
+ if (!this.selectedConnectionId) return;
327
+
328
+ // For project mode, require a project selection
329
+ if (this.viewMode === 'project' && !this.selectedProjectId) return;
330
+ // For group mode, require a group selection
331
+ if (this.viewMode === 'group' && !this.selectedGroupId) return;
332
+
333
+ this.isLoading = true;
334
+ try {
335
+ await appstate.dataStatePart.dispatchAction(appstate.fetchPipelinesAction, {
336
+ connectionId: this.selectedConnectionId,
337
+ projectId: this.viewMode === 'project' ? this.selectedProjectId : undefined,
338
+ viewMode: this.viewMode,
339
+ groupId: this.viewMode === 'group' ? this.selectedGroupId : undefined,
340
+ sortBy: this.sortBy,
341
+ timeRange: this.timeRange,
342
+ });
343
+ } finally {
344
+ this.isLoading = false;
345
+ }
346
+ }
347
+
348
+ // ---------------------------------------------------------------------------
349
+ // Pipeline log viewing
350
+ // ---------------------------------------------------------------------------
351
+
352
+ private async openPipelineLogs(pipeline: any) {
353
+ await appstate.dataStatePart.dispatchAction(appstate.fetchPipelineJobsAction, {
354
+ connectionId: pipeline.connectionId || this.selectedConnectionId,
355
+ projectId: pipeline.projectId,
356
+ pipelineId: pipeline.id,
357
+ });
358
+
359
+ const jobs = appstate.dataStatePart.getState().pipelineJobs;
360
+ let activeJobId: string | null = null;
361
+
362
+ const modal = await plugins.deesCatalog.DeesModal.createAndShow({
363
+ heading: `Pipeline #${pipeline.id} - Logs`,
364
+ content: html`
365
+ <style>
366
+ .log-container {
367
+ display: flex;
368
+ gap: 0;
369
+ min-height: 400px;
370
+ max-height: 70vh;
371
+ color: #ccc;
372
+ font-family: 'Intel One Mono', 'Fira Code', monospace;
373
+ }
374
+ .job-list {
375
+ width: 220px;
376
+ flex-shrink: 0;
377
+ border-right: 1px solid #333;
378
+ overflow-y: auto;
379
+ }
380
+ .job-entry {
381
+ padding: 10px 14px;
382
+ cursor: pointer;
383
+ border-bottom: 1px solid #282828;
384
+ transition: background 0.15s;
385
+ }
386
+ .job-entry:hover {
387
+ background: #1a1a2e;
388
+ }
389
+ .job-entry.active {
390
+ background: #1a2a3a;
391
+ border-left: 3px solid #00acff;
392
+ padding-left: 11px;
393
+ }
394
+ .job-name {
395
+ font-weight: 600;
396
+ font-size: 13px;
397
+ color: #eee;
398
+ }
399
+ .job-meta {
400
+ font-size: 11px;
401
+ color: #888;
402
+ margin-top: 3px;
403
+ }
404
+ .job-status-badge {
405
+ display: inline-block;
406
+ padding: 1px 6px;
407
+ border-radius: 3px;
408
+ font-size: 10px;
409
+ font-weight: 600;
410
+ text-transform: uppercase;
411
+ }
412
+ .job-status-badge.running { background: #1a2a3a; color: #00acff; }
413
+ .job-status-badge.success { background: #1a3a1a; color: #00ff88; }
414
+ .job-status-badge.failed { background: #3a1a1a; color: #ff4444; }
415
+ .job-status-badge.pending { background: #3a3a1a; color: #ffaa00; }
416
+ .job-status-badge.canceled { background: #2a2a2a; color: #999; }
417
+ .log-output {
418
+ flex: 1;
419
+ overflow: auto;
420
+ background: #0d0d0d;
421
+ padding: 12px;
422
+ }
423
+ .log-output pre {
424
+ margin: 0;
425
+ white-space: pre-wrap;
426
+ word-break: break-all;
427
+ font-family: 'Intel One Mono', 'Fira Code', monospace;
428
+ font-size: 12px;
429
+ line-height: 1.6;
430
+ color: #ccc;
431
+ }
432
+ .no-log {
433
+ color: #666;
434
+ padding: 40px 20px;
435
+ text-align: center;
436
+ font-size: 13px;
437
+ }
438
+ </style>
439
+ <div class="log-container">
440
+ <div class="job-list">
441
+ ${jobs.map((job: any) => html`
442
+ <div
443
+ class="job-entry"
444
+ data-job-id="${job.id}"
445
+ @click=${async (e: Event) => {
446
+ // Update active state visually
447
+ const container = (e.target as HTMLElement).closest('.log-container');
448
+ container?.querySelectorAll('.job-entry').forEach((el: Element) => el.classList.remove('active'));
449
+ (e.target as HTMLElement).closest('.job-entry')?.classList.add('active');
450
+ activeJobId = job.id;
451
+ await this.selectJobForLog(job, pipeline, container);
452
+ }}
453
+ >
454
+ <div class="job-name">${job.name}</div>
455
+ <div class="job-meta">
456
+ <span class="job-status-badge ${job.status}">${job.status}</span>
457
+ ${job.stage ? ` ${job.stage}` : ''}
458
+ ${job.duration ? ` - ${Math.round(job.duration)}s` : ''}
459
+ </div>
460
+ </div>
461
+ `)}
462
+ ${jobs.length === 0 ? html`<div class="no-log">No jobs found.</div>` : ''}
463
+ </div>
464
+ <div class="log-output">
465
+ <pre class="job-log-pre"><span class="no-log">Select a job to view its log.</span></pre>
466
+ </div>
467
+ </div>
468
+ `,
469
+ menuOptions: [
470
+ {
471
+ name: 'Close',
472
+ action: async (modalRef: any) => {
473
+ this.stopLogPolling();
474
+ modalRef.destroy();
475
+ },
476
+ },
477
+ ],
478
+ });
479
+ }
480
+
481
+ private async selectJobForLog(job: any, pipeline: any, container: Element | null) {
482
+ this.stopLogPolling();
483
+
484
+ // Fetch initial log
485
+ await this.fetchAndDisplayLog(job, pipeline, container);
486
+
487
+ // If job is running/pending, poll every 3 seconds
488
+ if (job.status === 'running' || job.status === 'pending' || job.status === 'waiting') {
489
+ this._logPollInterval = setInterval(async () => {
490
+ await this.fetchAndDisplayLog(job, pipeline, container);
491
+ }, 3000);
492
+ }
493
+ }
494
+
495
+ private async fetchAndDisplayLog(job: any, pipeline: any, container: Element | null) {
496
+ try {
497
+ await appstate.dataStatePart.dispatchAction(appstate.fetchJobLogAction, {
498
+ connectionId: pipeline.connectionId || this.selectedConnectionId,
499
+ projectId: pipeline.projectId,
500
+ jobId: job.id,
501
+ });
502
+
503
+ const log = appstate.dataStatePart.getState().currentJobLog;
504
+ const pre = container?.querySelector('.job-log-pre');
505
+ if (pre) {
506
+ pre.textContent = log || '(No output yet)';
507
+ // Auto-scroll to bottom
508
+ const logOutput = pre.closest('.log-output');
509
+ if (logOutput) {
510
+ logOutput.scrollTop = logOutput.scrollHeight;
511
+ }
512
+ }
513
+ } catch (err) {
514
+ console.error('Failed to fetch job log:', err);
515
+ }
516
+ }
517
+
518
+ private stopLogPolling() {
519
+ if (this._logPollInterval !== null) {
520
+ clearInterval(this._logPollInterval);
521
+ this._logPollInterval = null;
522
+ }
523
+ }
524
+
525
+ // ---------------------------------------------------------------------------
526
+ // Legacy job view (accessible via context menu)
527
+ // ---------------------------------------------------------------------------
528
+
529
+ private async viewJobs(pipeline: any) {
530
+ await appstate.dataStatePart.dispatchAction(appstate.fetchPipelineJobsAction, {
531
+ connectionId: pipeline.connectionId || this.selectedConnectionId,
532
+ projectId: pipeline.projectId,
533
+ pipelineId: pipeline.id,
534
+ });
535
+
536
+ const jobs = appstate.dataStatePart.getState().pipelineJobs;
537
+ await plugins.deesCatalog.DeesModal.createAndShow({
538
+ heading: `Pipeline #${pipeline.id} - Jobs`,
539
+ content: html`
540
+ <style>
541
+ .jobs-list { color: #fff; }
542
+ .job-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #333; }
543
+ .job-name { font-weight: 600; }
544
+ .job-status { text-transform: uppercase; font-size: 12px; }
545
+ </style>
546
+ <div class="jobs-list">
547
+ ${jobs.map((job: any) => html`
548
+ <div class="job-item">
549
+ <span class="job-name">${job.name} (${job.stage})</span>
550
+ <span class="job-status">${job.status} - ${job.duration ? `${Math.round(job.duration)}s` : '-'}</span>
551
+ </div>
552
+ `)}
553
+ ${jobs.length === 0 ? html`<p>No jobs found.</p>` : ''}
554
+ </div>
555
+ `,
556
+ menuOptions: [
557
+ { name: 'Close', action: async (modal: any) => { modal.destroy(); } },
558
+ ],
559
+ });
560
+ }
561
+ }
@@ -0,0 +1,149 @@
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-projects')
15
+ export class GitopsViewProjects extends DeesElement {
16
+ @state()
17
+ accessor connectionsState: appstate.IConnectionsState = {
18
+ connections: [],
19
+ activeConnectionId: null,
20
+ };
21
+
22
+ @state()
23
+ accessor dataState: appstate.IDataState = {
24
+ projects: [],
25
+ groups: [],
26
+ secrets: [],
27
+ pipelines: [],
28
+ pipelineJobs: [],
29
+ currentJobLog: '',
30
+ };
31
+
32
+ @state()
33
+ accessor selectedConnectionId: string = '';
34
+
35
+ private _autoRefreshHandler: () => void;
36
+
37
+ constructor() {
38
+ super();
39
+ const connSub = appstate.connectionsStatePart
40
+ .select((s) => s)
41
+ .subscribe((s) => { this.connectionsState = s; });
42
+ this.rxSubscriptions.push(connSub);
43
+
44
+ const dataSub = appstate.dataStatePart
45
+ .select((s) => s)
46
+ .subscribe((s) => { this.dataState = s; });
47
+ this.rxSubscriptions.push(dataSub);
48
+
49
+ this._autoRefreshHandler = () => this.handleAutoRefresh();
50
+ document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
51
+ }
52
+
53
+ public override disconnectedCallback() {
54
+ super.disconnectedCallback();
55
+ document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
56
+ }
57
+
58
+ private handleAutoRefresh(): void {
59
+ this.loadProjects();
60
+ }
61
+
62
+ public static styles = [
63
+ cssManager.defaultStyles,
64
+ viewHostCss,
65
+ ];
66
+
67
+ public render(): TemplateResult {
68
+ const connectionOptions = this.connectionsState.connections.map((c) => ({
69
+ option: `${c.name} (${c.providerType})`,
70
+ key: c.id,
71
+ }));
72
+
73
+ return html`
74
+ <div class="view-title">Projects</div>
75
+ <div class="view-description">Browse projects from your connected providers</div>
76
+ <div class="toolbar">
77
+ <dees-input-dropdown
78
+ .label=${'Connection'}
79
+ .options=${connectionOptions}
80
+ .selectedOption=${connectionOptions.find((o) => o.key === this.selectedConnectionId) || connectionOptions[0]}
81
+ @selectedOption=${(e: CustomEvent) => {
82
+ this.selectedConnectionId = e.detail.key;
83
+ this.loadProjects();
84
+ }}
85
+ ></dees-input-dropdown>
86
+ <dees-button @click=${() => this.loadProjects()}>Refresh</dees-button>
87
+ </div>
88
+ <dees-table
89
+ .heading1=${'Projects'}
90
+ .heading2=${'Repositories from the selected connection'}
91
+ .data=${this.dataState.projects}
92
+ .displayFunction=${(item: any) => ({
93
+ Name: item.name,
94
+ Path: item.fullPath,
95
+ Visibility: item.visibility,
96
+ Branch: item.defaultBranch,
97
+ 'Last Activity': item.lastActivity ? new Date(item.lastActivity).toLocaleDateString() : '-',
98
+ })}
99
+ .dataActions=${[
100
+ {
101
+ name: 'View Secrets',
102
+ iconName: 'lucide:key',
103
+ type: ['inRow', 'contextmenu'],
104
+ actionFunc: async ({ item }: any) => {
105
+ appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, {
106
+ view: 'secrets',
107
+ navigationContext: {
108
+ connectionId: this.selectedConnectionId,
109
+ scope: 'project',
110
+ scopeId: item.id,
111
+ },
112
+ });
113
+ },
114
+ },
115
+ {
116
+ name: 'View Pipelines',
117
+ iconName: 'lucide:play',
118
+ type: ['inRow', 'contextmenu'],
119
+ actionFunc: async ({ item }: any) => {
120
+ appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, {
121
+ view: 'pipelines',
122
+ navigationContext: {
123
+ connectionId: this.selectedConnectionId,
124
+ projectId: item.id,
125
+ },
126
+ });
127
+ },
128
+ },
129
+ ]}
130
+ ></dees-table>
131
+ `;
132
+ }
133
+
134
+ async firstUpdated() {
135
+ await appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
136
+ const conns = appstate.connectionsStatePart.getState().connections;
137
+ if (conns.length > 0 && !this.selectedConnectionId) {
138
+ this.selectedConnectionId = conns[0].id;
139
+ await this.loadProjects();
140
+ }
141
+ }
142
+
143
+ private async loadProjects() {
144
+ if (!this.selectedConnectionId) return;
145
+ await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, {
146
+ connectionId: this.selectedConnectionId,
147
+ });
148
+ }
149
+ }