1 | import * as wmill from "windmill-client"; |
2 | import createClient, { type Middleware } from "openapi-fetch"; |
3 |
|
4 | |
5 | export interface NextcloudWebhookPayload { |
6 | event: NextcloudEvent; |
7 | user: NextcloudUser; |
8 | time: number; |
9 | } |
10 |
|
11 | export interface NextcloudEvent { |
12 | node: NextcloudNode; |
13 | class: string; |
14 | } |
15 |
|
16 | export interface NextcloudNode { |
17 | id: number; |
18 | path: string; |
19 | |
20 | storage?: string; |
21 | size?: number; |
22 | name?: string; |
23 | } |
24 |
|
25 | export interface NextcloudUser { |
26 | uid: string; |
27 | displayName?: string; |
28 | } |
29 |
|
30 | |
31 | * Nextcloud Trigger Preprocessor |
32 | * |
33 | * This function runs BEFORE the main function. |
34 | * |
35 | * It processes raw Nextcloud webhook trigger data before passing it to `main()`. |
36 | * Supports the available Nextcloud events: |
37 | * - FormSubmittedEvent: Form submissions from Nextcloud Forms |
38 | * - NodeWrittenEvent: File/folder operations (create, update, delete) |
39 | * |
40 | * The returned object determines `main()` parameters based on event type. |
41 | * |
42 | * @param event - Nextcloud trigger data and details |
43 | * @returns Processed data for `main()` |
44 | */ |
45 | export async function preprocessor( |
46 | event: { |
47 | kind: 'nextcloud', |
48 | headers: Record<string, string>, |
49 | payload: NextcloudWebhookPayload, |
50 | }, |
51 | ) { |
52 | if (event.kind === 'nextcloud') { |
53 | const payload = event.payload; |
54 |
|
55 | const baseEvent = { |
56 | eventType: payload.event.class, |
57 | userId: payload.user.uid, |
58 | timestamp: new Date(payload.time * 1000).toISOString(), |
59 | headers: event.headers || {}, |
60 | }; |
61 |
|
62 | |
63 | const eventTypeMatch = payload.event.class.match(/(\w+)Event$/); |
64 | const eventType = eventTypeMatch ? eventTypeMatch[1] : 'Unknown'; |
65 |
|
66 | |
67 | switch (eventType) { |
68 | case 'FormSubmitted': |
69 | return { |
70 | ...baseEvent, |
71 | category: 'form', |
72 | user: { |
73 | uid: payload.user.uid, |
74 | displayName: payload.user.displayName || '' |
75 | }, |
76 | rawPayload: payload |
77 | }; |
78 | |
79 | case 'NodeWritten': |
80 | return { |
81 | ...baseEvent, |
82 | category: 'file', |
83 | user: { |
84 | uid: payload.user.uid, |
85 | displayName: payload.user.displayName || '' |
86 | }, |
87 | node: { |
88 | id: payload.event.node.id, |
89 | path: payload.event.node.path, |
90 | storage: payload.event.node.storage, |
91 | size: payload.event.node.size, |
92 | name: payload.event.node.name |
93 | }, |
94 | rawPayload: payload |
95 | }; |
96 | |
97 | default: |
98 | return { |
99 | ...baseEvent, |
100 | category: 'other', |
101 | eventClass: payload.event.class, |
102 | data: payload.event, |
103 | rawPayload: payload |
104 | }; |
105 | } |
106 | } |
107 |
|
108 | throw new Error(`Expected nextcloud trigger kind, got: ${event.kind}`); |
109 | } |
110 |
|
111 | |
112 | * Main Function - Handles processed Nextcloud events |
113 | * |
114 | * Called AFTER `preprocessor()`, with its return values. |
115 | * |
116 | * This example demonstrates integrating with Nextcloud APIs using openapi-fetch |
117 | * and windmill-client, following the official Nextcloud Hub example patterns. |
118 | * |
119 | * @param eventType - Type of Nextcloud event class name |
120 | * @param category - Event category ('form', 'file', or 'other') |
121 | * @param userId - ID of the user who triggered the event |
122 | * @param user - User information |
123 | * @param node - Node information (for file events) |
124 | * @param eventClass - Full event class name (for other events) |
125 | * @param data - Generic event data (for other events) |
126 | * @param timestamp - Event timestamp |
127 | * @param rawPayload - Raw event payload from Nextcloud |
128 | * @param nextcloud_resource - Nextcloud resource containing server URL and credentials |
129 | */ |
130 | export async function main( |
131 | eventType: string, |
132 | category: string, |
133 | userId?: string, |
134 | user?: { |
135 | uid: string; |
136 | displayName: string; |
137 | }, |
138 | node?: { |
139 | id?: number; |
140 | path?: string; |
141 | storage?: string; |
142 | size?: number; |
143 | name?: string; |
144 | }, |
145 | eventClass?: string, |
146 | data?: any, |
147 | timestamp?: string, |
148 | rawPayload?: NextcloudWebhookPayload, |
149 | nextcloud_resource?: string |
150 | ) { |
151 |
|
152 | const resource = await wmill.getResource(nextcloud_resource) |
153 | const client = createClient({ baseUrl: resource.baseUrl }); |
154 | |
155 | console.log(`Processing Nextcloud event: ${eventType}`); |
156 | console.log(`Category: ${category}`); |
157 | console.log(`User: ${user?.uid} (${user?.displayName})`); |
158 | console.log(`Timestamp: ${timestamp}`); |
159 |
|
160 | try { |
161 | |
162 | const authMiddleware: Middleware = { |
163 | async onRequest({ request }: { request: Request }) { |
164 | |
165 | request.headers.set( |
166 | "Authorization", |
167 | `Basic ${btoa(resource.username + ':' + resource.password)}` |
168 | ); |
169 |
|
170 | |
171 | request.headers.set("OCS-APIRequest", "true"); |
172 | request.headers.set("Content-Type", "application/json"); |
173 | request.headers.set("Accept", "application/json"); |
174 |
|
175 | return request; |
176 | }, |
177 | }; |
178 |
|
179 | client.use(authMiddleware); |
180 | console.log(`Connected to Nextcloud at: ${resource.baseUrl}`); |
181 | } catch (error) { |
182 | console.error('Failed to setup Nextcloud API client:', error); |
183 | return { success: false, error: 'Failed to configure Nextcloud API' }; |
184 | } |
185 |
|
186 | |
187 | switch (category) { |
188 | case 'form': |
189 | console.log(`Form event processed: ${eventType}`); |
190 | break; |
191 |
|
192 | case 'file': |
193 | console.log(`File event: ${node?.path} (ID: ${node?.id})`); |
194 | console.log(`Node details:`, { |
195 | path: node?.path, |
196 | size: node?.size, |
197 | storage: node?.storage, |
198 | name: node?.name |
199 | }); |
200 | break; |
201 |
|
202 | case 'other': |
203 | console.log(`Other event type: ${eventClass}`); |
204 | console.log('Event data:', data); |
205 |
|
206 | if (client && userId) { |
207 | try { |
208 | |
209 | console.log('Logging unknown event to system'); |
210 | } catch (error) { |
211 | console.error('Failed to log unknown event:', error); |
212 | } |
213 | } |
214 | break; |
215 | } |
216 |
|
217 | |
218 | return { |
219 | success: true, |
220 | eventType, |
221 | category, |
222 | userId, |
223 | timestamp, |
224 | processed: true, |
225 | message: `Successfully processed ${eventType} event`, |
226 | eventDetails: { |
227 | user: user || null, |
228 | node: node || null, |
229 | data: data || null, |
230 | eventClass: eventClass || null |
231 | } |
232 | }; |
233 | } |