@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,424 @@
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-jobs')
15
+ export class GitopsViewJobs extends DeesElement {
16
+ @state()
17
+ accessor jobsState: appstate.IJobsState = { jobs: [] };
18
+
19
+ @state()
20
+ accessor connectionsState: appstate.IConnectionsState = {
21
+ connections: [],
22
+ activeConnectionId: null,
23
+ };
24
+
25
+ private _autoRefreshHandler: () => void;
26
+ private _jobLogHandler: (e: Event) => void;
27
+
28
+ constructor() {
29
+ super();
30
+ const jobsSub = appstate.jobsStatePart
31
+ .select((s) => s)
32
+ .subscribe((s) => { this.jobsState = s; });
33
+ this.rxSubscriptions.push(jobsSub);
34
+
35
+ const connSub = appstate.connectionsStatePart
36
+ .select((s) => s)
37
+ .subscribe((s) => { this.connectionsState = s; });
38
+ this.rxSubscriptions.push(connSub);
39
+
40
+ this._autoRefreshHandler = () => this.refresh();
41
+ document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
42
+
43
+ this._jobLogHandler = (e: Event) => {
44
+ const entry = (e as CustomEvent).detail;
45
+ if (!entry) return;
46
+ const chartLog = this.shadowRoot?.querySelector('dees-chart-log') as any;
47
+ if (chartLog?.addLog) {
48
+ chartLog.addLog(entry.level, entry.message, entry.source);
49
+ }
50
+ };
51
+ document.addEventListener('gitops-job-log-entry', this._jobLogHandler);
52
+ }
53
+
54
+ public override disconnectedCallback() {
55
+ super.disconnectedCallback();
56
+ document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
57
+ document.removeEventListener('gitops-job-log-entry', this._jobLogHandler);
58
+ }
59
+
60
+ public static styles = [
61
+ cssManager.defaultStyles,
62
+ viewHostCss,
63
+ css`
64
+ .status-active { color: #2ecc71; }
65
+ .status-paused { color: #f39c12; }
66
+ .status-error { color: #e74c3c; }
67
+ `,
68
+ ];
69
+
70
+ public render(): TemplateResult {
71
+ return html`
72
+ <div class="view-title">Jobs</div>
73
+ <div class="view-description">Scheduled background tasks for automation</div>
74
+ <div class="toolbar">
75
+ <dees-button @click=${() => this.addJob()}>Add Job</dees-button>
76
+ <dees-button @click=${() => this.refresh()}>Refresh</dees-button>
77
+ </div>
78
+ <dees-table
79
+ .heading1=${'Jobs'}
80
+ .heading2=${'Manage scheduled automation tasks'}
81
+ .data=${this.jobsState.jobs}
82
+ .displayFunction=${(item: any) => ({
83
+ Name: item.name,
84
+ Type: this.formatJobType(item.jobType),
85
+ Source: item.autoBookstackDocsConfig?.sourceConnectionIds?.map((id: string) => this.getConnectionName(id)).join(', ') || '-',
86
+ 'BookStack': item.autoBookstackDocsConfig?.bookstackTarget?.baseUrl || '-',
87
+ Interval: `${item.intervalMinutes}min`,
88
+ Status: item.status,
89
+ 'Last Run': item.lastRunAt ? new Date(item.lastRunAt).toLocaleString() : 'Never',
90
+ 'Duration': item.lastRunDurationMs ? `${(item.lastRunDurationMs / 1000).toFixed(1)}s` : '-',
91
+ Error: item.lastRunError ? item.lastRunError.slice(0, 50) : '-',
92
+ })}
93
+ .dataActions=${[
94
+ {
95
+ name: 'Trigger Now',
96
+ iconName: 'lucide:play',
97
+ type: ['inRow', 'contextmenu'],
98
+ actionFunc: async ({ item }: any) => { await this.triggerJob(item); },
99
+ },
100
+ {
101
+ name: 'Edit',
102
+ iconName: 'lucide:edit',
103
+ type: ['inRow', 'contextmenu'],
104
+ actionFunc: async ({ item }: any) => { await this.editJob(item); },
105
+ },
106
+ {
107
+ name: 'Pause/Resume',
108
+ iconName: 'lucide:pauseCircle',
109
+ type: ['inRow', 'contextmenu'],
110
+ actionFunc: async ({ item }: any) => { await this.togglePause(item); },
111
+ },
112
+ {
113
+ name: 'Delete',
114
+ iconName: 'lucide:trash2',
115
+ type: ['inRow', 'contextmenu'],
116
+ actionFunc: async ({ item }: any) => { await this.deleteJob(item); },
117
+ },
118
+ ]}
119
+ ></dees-table>
120
+ <dees-chart-log
121
+ .label=${'Job Activity Log'}
122
+ .autoScroll=${true}
123
+ .maxEntries=${500}
124
+ ></dees-chart-log>
125
+ `;
126
+ }
127
+
128
+ async firstUpdated() {
129
+ await appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);
130
+ await appstate.jobsStatePart.dispatchAction(appstate.fetchJobsAction, null);
131
+ await appstate.initJobLogSocket();
132
+ await this.loadExistingLogs();
133
+ }
134
+
135
+ private async loadExistingLogs() {
136
+ try {
137
+ const logs = await appstate.fetchJobLogs(200);
138
+ const chartLog = this.shadowRoot?.querySelector('dees-chart-log') as any;
139
+ if (chartLog?.updateLog && logs.length > 0) {
140
+ chartLog.updateLog(
141
+ logs.map((entry: any) => ({
142
+ timestamp: new Date(entry.timestamp).toISOString(),
143
+ level: entry.level,
144
+ message: entry.message,
145
+ source: entry.source,
146
+ })),
147
+ );
148
+ }
149
+ } catch (err) {
150
+ console.error('Failed to load job logs:', err);
151
+ }
152
+ }
153
+
154
+ private formatJobType(type: string): string {
155
+ switch (type) {
156
+ case 'autobookstackdocs': return 'Auto BookStack Docs';
157
+ default: return type;
158
+ }
159
+ }
160
+
161
+ private getConnectionName(id: string): string {
162
+ const conn = this.connectionsState.connections.find((c) => c.id === id);
163
+ return conn ? conn.name : id;
164
+ }
165
+
166
+ private async refresh() {
167
+ await appstate.jobsStatePart.dispatchAction(appstate.fetchJobsAction, null);
168
+ }
169
+
170
+ private async triggerJob(item: any) {
171
+ await appstate.jobsStatePart.dispatchAction(appstate.triggerJobAction, { jobId: item.id });
172
+ }
173
+
174
+ private async togglePause(item: any) {
175
+ const paused = item.status !== 'paused';
176
+ await appstate.jobsStatePart.dispatchAction(appstate.pauseJobAction, {
177
+ jobId: item.id,
178
+ paused,
179
+ });
180
+ }
181
+
182
+ private async deleteJob(item: any) {
183
+ await plugins.deesCatalog.DeesModal.createAndShow({
184
+ heading: 'Delete Job',
185
+ content: html`<p style="color: #fff;">Are you sure you want to delete job "${item.name}"?</p>`,
186
+ menuOptions: [
187
+ { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
188
+ {
189
+ name: 'Delete',
190
+ action: async (modal: any) => {
191
+ await appstate.jobsStatePart.dispatchAction(appstate.deleteJobAction, { jobId: item.id });
192
+ modal.destroy();
193
+ },
194
+ },
195
+ ],
196
+ });
197
+ }
198
+
199
+ private async addJob() {
200
+ const connections = this.connectionsState.connections;
201
+
202
+ await plugins.deesCatalog.DeesModal.createAndShow({
203
+ heading: 'Add Job',
204
+ content: html`
205
+ <style>.form-row { margin-bottom: 16px; }</style>
206
+ <div class="form-row">
207
+ <dees-input-text .label=${'Job Name'} .key=${'name'}></dees-input-text>
208
+ </div>
209
+ <div class="form-row">
210
+ <dees-input-dropdown
211
+ .label=${'Job Type'}
212
+ .key=${'jobType'}
213
+ .options=${[{ option: 'Auto BookStack Docs', key: 'autobookstackdocs' }]}
214
+ .selectedOption=${{ option: 'Auto BookStack Docs', key: 'autobookstackdocs' }}
215
+ ></dees-input-dropdown>
216
+ </div>
217
+ <div class="form-row">
218
+ <dees-input-dropdown
219
+ .label=${'Source Connection'}
220
+ .key=${'sourceConnection'}
221
+ .options=${connections.map((c) => ({ option: `${c.name} (${c.providerType})`, key: c.id }))}
222
+ .selectedOption=${connections.length > 0 ? { option: `${connections[0].name} (${connections[0].providerType})`, key: connections[0].id } : undefined}
223
+ ></dees-input-dropdown>
224
+ </div>
225
+ <div class="form-row">
226
+ <dees-input-text .label=${'BookStack URL'} .key=${'bookstackBaseUrl'}></dees-input-text>
227
+ </div>
228
+ <div class="form-row">
229
+ <dees-input-text .label=${'BookStack Token ID'} .key=${'bookstackTokenId'}></dees-input-text>
230
+ </div>
231
+ <div class="form-row">
232
+ <dees-input-text .label=${'BookStack Token Secret'} .key=${'bookstackTokenSecret'} type="password"></dees-input-text>
233
+ </div>
234
+ <div class="form-row">
235
+ <dees-input-text .label=${'Root Files (comma-separated)'} .key=${'filePaths'} .value=${'readme.md, changelog.md'} .description=${'Files in repo root to sync as pages'}></dees-input-text>
236
+ </div>
237
+ <div class="form-row">
238
+ <dees-input-text .label=${'Doc Directories (comma-separated)'} .key=${'docDirs'} .value=${'docs'} .description=${'Directories to scan for .md files'}></dees-input-text>
239
+ </div>
240
+ <div class="form-row">
241
+ <dees-input-text .label=${'Include Groups (optional, comma-separated)'} .key=${'includeGroups'}></dees-input-text>
242
+ </div>
243
+ <div class="form-row">
244
+ <dees-input-text .label=${'Exclude Groups (optional, comma-separated)'} .key=${'excludeGroups'}></dees-input-text>
245
+ </div>
246
+ <div class="form-row">
247
+ <dees-input-text .label=${'Interval (minutes)'} .key=${'intervalMinutes'} .value=${'60'}></dees-input-text>
248
+ </div>
249
+ <div class="form-row">
250
+ <dees-input-checkbox .label=${'Propagate Deletes'} .key=${'propagateDeletes'} .value=${false} .description=${'Remove stale shelves/books/pages from BookStack'}></dees-input-checkbox>
251
+ </div>
252
+ <div class="form-row">
253
+ <dees-input-checkbox .label=${'Sync Visibility'} .key=${'syncVisibility'} .value=${false} .description=${'Map git private/public to BookStack permissions'}></dees-input-checkbox>
254
+ </div>
255
+ <div class="form-row">
256
+ <dees-input-text .label=${'Private Role ID (BookStack)'} .key=${'privateRoleId'} .description=${'BookStack role ID that gets access to private repos'}></dees-input-text>
257
+ </div>
258
+ `,
259
+ menuOptions: [
260
+ { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
261
+ {
262
+ name: 'Create',
263
+ action: async (modal: any) => {
264
+ const inputs = modal.shadowRoot.querySelectorAll('dees-input-text');
265
+ const data: Record<string, string> = {};
266
+ for (const input of inputs) { data[(input as any).key] = (input as any).value || ''; }
267
+
268
+ // Read source connection from dropdown
269
+ const dropdowns = modal.shadowRoot.querySelectorAll('dees-input-dropdown');
270
+ let sourceConnectionId = '';
271
+ for (const dd of dropdowns) {
272
+ if ((dd as any).key === 'sourceConnection') {
273
+ sourceConnectionId = (dd as any).selectedOption?.key || '';
274
+ }
275
+ }
276
+
277
+ // Read checkboxes
278
+ const checkboxes = modal.shadowRoot.querySelectorAll('dees-input-checkbox');
279
+ const boolData: Record<string, boolean> = {};
280
+ for (const cb of checkboxes) { boolData[(cb as any).key] = !!(cb as any).value; }
281
+
282
+ if (!data.name || !data.bookstackBaseUrl || !data.bookstackTokenId || !data.bookstackTokenSecret || !sourceConnectionId) return;
283
+
284
+ const splitCsv = (s: string) => s ? s.split(',').map((v: string) => v.trim()).filter(Boolean) : [];
285
+
286
+ await appstate.jobsStatePart.dispatchAction(appstate.createJobAction, {
287
+ name: data.name,
288
+ jobType: 'autobookstackdocs' as const,
289
+ intervalMinutes: parseInt(data.intervalMinutes, 10) || 60,
290
+ autoBookstackDocsConfig: {
291
+ sourceConnectionIds: [sourceConnectionId],
292
+ bookstackTarget: {
293
+ baseUrl: data.bookstackBaseUrl.replace(/\/+$/, ''),
294
+ tokenId: data.bookstackTokenId,
295
+ tokenSecret: data.bookstackTokenSecret,
296
+ },
297
+ filePaths: splitCsv(data.filePaths).length > 0 ? splitCsv(data.filePaths) : ['readme.md', 'changelog.md'],
298
+ docDirs: splitCsv(data.docDirs).length > 0 ? splitCsv(data.docDirs) : ['docs'],
299
+ includeGroups: splitCsv(data.includeGroups).length > 0 ? splitCsv(data.includeGroups) : undefined,
300
+ excludeGroups: splitCsv(data.excludeGroups).length > 0 ? splitCsv(data.excludeGroups) : undefined,
301
+ propagateDeletes: boolData.propagateDeletes || false,
302
+ syncVisibility: boolData.syncVisibility || false,
303
+ privateRoleId: data.privateRoleId ? parseInt(data.privateRoleId, 10) : undefined,
304
+ },
305
+ });
306
+ modal.destroy();
307
+ },
308
+ },
309
+ ],
310
+ });
311
+ }
312
+
313
+ private async editJob(item: any) {
314
+ const connections = this.connectionsState.connections;
315
+ const config = item.autoBookstackDocsConfig;
316
+
317
+ await plugins.deesCatalog.DeesModal.createAndShow({
318
+ heading: `Edit: ${item.name}`,
319
+ content: html`
320
+ <style>.form-row { margin-bottom: 16px; }</style>
321
+ <div class="form-row">
322
+ <dees-input-text .label=${'Job Name'} .key=${'name'} .value=${item.name}></dees-input-text>
323
+ </div>
324
+ <div class="form-row">
325
+ <dees-input-dropdown
326
+ .label=${'Source Connection'}
327
+ .key=${'sourceConnection'}
328
+ .options=${connections.map((c) => ({ option: `${c.name} (${c.providerType})`, key: c.id }))}
329
+ .selectedOption=${(() => {
330
+ const currentId = config?.sourceConnectionIds?.[0];
331
+ const conn = connections.find((c) => c.id === currentId);
332
+ return conn ? { option: `${conn.name} (${conn.providerType})`, key: conn.id } : (connections.length > 0 ? { option: `${connections[0].name} (${connections[0].providerType})`, key: connections[0].id } : undefined);
333
+ })()}
334
+ ></dees-input-dropdown>
335
+ </div>
336
+ <div class="form-row">
337
+ <dees-input-text .label=${'BookStack URL'} .key=${'bookstackBaseUrl'} .value=${config?.bookstackTarget?.baseUrl || ''}></dees-input-text>
338
+ </div>
339
+ <div class="form-row">
340
+ <dees-input-text .label=${'BookStack Token ID'} .key=${'bookstackTokenId'} .value=${config?.bookstackTarget?.tokenId || ''}></dees-input-text>
341
+ </div>
342
+ <div class="form-row">
343
+ <dees-input-text .label=${'BookStack Token Secret (leave empty to keep current)'} .key=${'bookstackTokenSecret'} type="password"></dees-input-text>
344
+ </div>
345
+ <div class="form-row">
346
+ <dees-input-text .label=${'Root Files (comma-separated)'} .key=${'filePaths'} .value=${config?.filePaths?.join(', ') || 'readme.md'} .description=${'Files in repo root to sync as pages'}></dees-input-text>
347
+ </div>
348
+ <div class="form-row">
349
+ <dees-input-text .label=${'Doc Directories (comma-separated)'} .key=${'docDirs'} .value=${config?.docDirs?.join(', ') || 'docs'} .description=${'Directories to scan for .md files'}></dees-input-text>
350
+ </div>
351
+ <div class="form-row">
352
+ <dees-input-text .label=${'Include Groups (optional, comma-separated)'} .key=${'includeGroups'} .value=${config?.includeGroups?.join(', ') || ''}></dees-input-text>
353
+ </div>
354
+ <div class="form-row">
355
+ <dees-input-text .label=${'Exclude Groups (optional, comma-separated)'} .key=${'excludeGroups'} .value=${config?.excludeGroups?.join(', ') || ''}></dees-input-text>
356
+ </div>
357
+ <div class="form-row">
358
+ <dees-input-text .label=${'Interval (minutes)'} .key=${'intervalMinutes'} .value=${String(item.intervalMinutes)}></dees-input-text>
359
+ </div>
360
+ <div class="form-row">
361
+ <dees-input-checkbox .label=${'Propagate Deletes'} .key=${'propagateDeletes'} .value=${config?.propagateDeletes || false} .description=${'Remove stale shelves/books/pages from BookStack'}></dees-input-checkbox>
362
+ </div>
363
+ <div class="form-row">
364
+ <dees-input-checkbox .label=${'Sync Visibility'} .key=${'syncVisibility'} .value=${config?.syncVisibility || false} .description=${'Map git private/public to BookStack permissions'}></dees-input-checkbox>
365
+ </div>
366
+ <div class="form-row">
367
+ <dees-input-text .label=${'Private Role ID (BookStack)'} .key=${'privateRoleId'} .value=${config?.privateRoleId ? String(config.privateRoleId) : ''} .description=${'BookStack role ID that gets access to private repos'}></dees-input-text>
368
+ </div>
369
+ `,
370
+ menuOptions: [
371
+ { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
372
+ {
373
+ name: 'Update',
374
+ action: async (modal: any) => {
375
+ const inputs = modal.shadowRoot.querySelectorAll('dees-input-text');
376
+ const data: Record<string, string> = {};
377
+ for (const input of inputs) { data[(input as any).key] = (input as any).value || ''; }
378
+
379
+ // Read source connection from dropdown
380
+ const dropdowns = modal.shadowRoot.querySelectorAll('dees-input-dropdown');
381
+ let sourceConnectionId = config?.sourceConnectionIds?.[0] || '';
382
+ for (const dd of dropdowns) {
383
+ if ((dd as any).key === 'sourceConnection') {
384
+ sourceConnectionId = (dd as any).selectedOption?.key || sourceConnectionId;
385
+ }
386
+ }
387
+
388
+ // Read checkboxes
389
+ const checkboxes = modal.shadowRoot.querySelectorAll('dees-input-checkbox');
390
+ const boolData: Record<string, boolean> = {};
391
+ for (const cb of checkboxes) { boolData[(cb as any).key] = !!(cb as any).value; }
392
+
393
+ const splitCsv = (s: string) => s ? s.split(',').map((v: string) => v.trim()).filter(Boolean) : [];
394
+
395
+ // Only send a new tokenSecret if the user typed one; empty means keep existing
396
+ const newTokenSecret = data.bookstackTokenSecret || '';
397
+
398
+ await appstate.jobsStatePart.dispatchAction(appstate.updateJobAction, {
399
+ jobId: item.id,
400
+ name: data.name || undefined,
401
+ intervalMinutes: data.intervalMinutes ? parseInt(data.intervalMinutes, 10) : undefined,
402
+ autoBookstackDocsConfig: {
403
+ sourceConnectionIds: [sourceConnectionId],
404
+ bookstackTarget: {
405
+ baseUrl: data.bookstackBaseUrl.replace(/\/+$/, ''),
406
+ tokenId: data.bookstackTokenId,
407
+ tokenSecret: newTokenSecret,
408
+ },
409
+ filePaths: splitCsv(data.filePaths).length > 0 ? splitCsv(data.filePaths) : ['readme.md', 'changelog.md'],
410
+ docDirs: splitCsv(data.docDirs).length > 0 ? splitCsv(data.docDirs) : ['docs'],
411
+ includeGroups: splitCsv(data.includeGroups).length > 0 ? splitCsv(data.includeGroups) : undefined,
412
+ excludeGroups: splitCsv(data.excludeGroups).length > 0 ? splitCsv(data.excludeGroups) : undefined,
413
+ propagateDeletes: boolData.propagateDeletes || false,
414
+ syncVisibility: boolData.syncVisibility || false,
415
+ privateRoleId: data.privateRoleId ? parseInt(data.privateRoleId, 10) : undefined,
416
+ },
417
+ });
418
+ modal.destroy();
419
+ },
420
+ },
421
+ ],
422
+ });
423
+ }
424
+ }