diff options
| author | Yuval Adam <_@yuv.al> | 2025-10-03 11:52:01 +0200 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2025-10-03 11:52:01 +0200 |
| commit | 7b6314f480d4f7a0932d6463e5341e21566a37c2 (patch) | |
| tree | 852174e337af4ec373e966b642dc59ca0a7e5c74 | |
| parent | dc4d65708c92cba5652597af8eb49d1297026e15 (diff) | |
Implement loading of unauthenticated radar GIS images
| -rw-r--r-- | astro.config.mjs | 5 | ||||
| -rw-r--r-- | package-lock.json | 84 | ||||
| -rw-r--r-- | package.json | 2 | ||||
| -rw-r--r-- | src/functions/scheduled.ts | 229 | ||||
| -rw-r--r-- | src/worker.ts | 18 | ||||
| -rw-r--r-- | worker-configuration.d.ts | 28 | ||||
| -rw-r--r-- | wrangler.jsonc | 10 |
7 files changed, 232 insertions, 144 deletions
diff --git a/astro.config.mjs b/astro.config.mjs index 2674216..fff01e2 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -18,7 +18,10 @@ export default defineConfig({ }, adapter: cloudflare({ - imageService: "compile" + imageService: "compile", + workerEntryPoint: { + path: 'src/worker.ts' + } }), devToolbar: { diff --git a/package-lock.json b/package-lock.json index caa9127..2729cdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "tailwindcss": "^4.1.13" }, "devDependencies": { - "wrangler": "^4.39.0" + "wrangler": "^4.41.0" } }, "node_modules/@astrojs/cloudflare": { @@ -1490,14 +1490,14 @@ } }, "node_modules/@cloudflare/unenv-preset": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.7.4.tgz", - "integrity": "sha512-KIjbu/Dt50zseJIoOOK5y4eYpSojD9+xxkePYVK1Rg9k/p/st4YyMtz1Clju/zrenJHrOH+AAcjNArOPMwH4Bw==", + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.7.5.tgz", + "integrity": "sha512-eB3UAIVhrvY+CMZrRXS/bAv5kWdNiH+dgwu+1M1S7keDeonxkfKIGVIrhcCLTkcqYlN30MPURPuVFUEzIWuuvg==", "dev": true, "license": "MIT OR Apache-2.0", "peerDependencies": { "unenv": "2.0.0-rc.21", - "workerd": "^1.20250912.0" + "workerd": "^1.20250924.0" }, "peerDependenciesMeta": { "workerd": { @@ -1506,9 +1506,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20250923.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250923.0.tgz", - "integrity": "sha512-CUyVkdTaREdT/wynh5/VX3prawWpYeoqGjcEyo920/HqXaRuA/owp9ijg1vh1rmHyxN0XvsjHuRwBfnrptGmrg==", + "version": "1.20251001.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20251001.0.tgz", + "integrity": "sha512-y1ST/cCscaRewWRnsHZdWbgiLJbki5UMGd0hMo/FLqjlztwPeDgQ5CGm5jMiCDdw/IBCpWxEukftPYR34rWNog==", "cpu": [ "x64" ], @@ -1523,9 +1523,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20250923.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250923.0.tgz", - "integrity": "sha512-wblU5WYlNRnrTMupeWFRoysJH/Y7d6h+Wc1G+GTmaMV6TcxyXj804Hk8Tk3jqvaS0SXmkh5sIQ38MBVrBs7sag==", + "version": "1.20251001.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20251001.0.tgz", + "integrity": "sha512-+z4QHHZ/Yix82zLFYS+ZS2UV09IENFPwDCEKUWfnrM9Km2jOOW3Ua4hJNob1EgQUYs8fFZo7k5O/tpwxMsSbbQ==", "cpu": [ "arm64" ], @@ -1540,9 +1540,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20250923.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250923.0.tgz", - "integrity": "sha512-WFP0KBJWhdDJWChIw3HJmrtYLNQDB8X9R3o548FcE5NiD05J0rI5Pnhno008lanjmXzia1lghWIQErfpjpmQzg==", + "version": "1.20251001.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20251001.0.tgz", + "integrity": "sha512-hGS+O2V9Mm2XjJUaB9ZHMA5asDUaDjKko42e+accbew0PQR7zrAl1afdII6hMqCLV4tk4GAjvhv281pN4g48rg==", "cpu": [ "x64" ], @@ -1557,9 +1557,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20250923.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250923.0.tgz", - "integrity": "sha512-h5VegEhn9CPfGnTc4JTFLGzR806naeIcKUHWi8ejc1hO38YthC6mBNsOjbZrSkp4B3H/kstuhuW16x2rGX0oBg==", + "version": "1.20251001.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20251001.0.tgz", + "integrity": "sha512-QYaMK+pRgt28N7CX1JlJ+ToegJF9LxzqdT7MjWqPgVj9D2WTyIhBVYl3wYjJRcgOlnn+DRt42+li4T64CPEeuA==", "cpu": [ "arm64" ], @@ -1574,9 +1574,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20250923.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250923.0.tgz", - "integrity": "sha512-S1E2Vm11ClrwHLqbVO59pKpxllHkq3APRdt3wCbVZNG7jGctxEYLH2uE0K1O1JBfbk2fKMaa+sbmtMpXDXi1tw==", + "version": "1.20251001.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20251001.0.tgz", + "integrity": "sha512-ospnDR/FlyRvrv9DSHuxDAXmzEBLDUiAHQrQHda1iUH9HqxnNQ8giz9VlPfq7NIRc7bQ1ZdIYPGLJOY4Q366Ng==", "cpu": [ "x64" ], @@ -1591,9 +1591,9 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "4.20250924.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20250924.0.tgz", - "integrity": "sha512-pi/OYCroYdwjFWbkciC5oYzlyimDF4ymNotDK0zpLNq91Ogz1IXnVBAYV7fCFAJ/zIxU0RiIBrJIOll/C0pR9Q==", + "version": "4.20251003.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20251003.0.tgz", + "integrity": "sha512-rtg7FqYVNHsltn1GOV+P3hQpVf1WDI3wH8Mj4IjK99fidJ4ttzUPuCX/uYE/PIF/HBPK66TqVwToO+GN4Lmx0Q==", "license": "MIT OR Apache-2.0" }, "node_modules/@cspotcode/source-map-support": { @@ -6105,9 +6105,9 @@ } }, "node_modules/miniflare": { - "version": "4.20250923.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250923.0.tgz", - "integrity": "sha512-CtO0w3tKr8rl5nS5TchYNGQaXuYLfl1T+IqKQiEoIRAUpVWdiziK49+mKV+Vz6yRENqHEGMYV8EjhfvmEHrJpA==", + "version": "4.20251001.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20251001.0.tgz", + "integrity": "sha512-OHd31D2LT8JH+85nVXClV0Z18jxirCohzKNAcZs/fgt4mIkUDtidX3VqR3ovAM0jWooNxrFhB9NSs3iDbiJF7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6119,7 +6119,7 @@ "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", - "workerd": "1.20250923.0", + "workerd": "1.20251001.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" @@ -8191,9 +8191,9 @@ } }, "node_modules/workerd": { - "version": "1.20250923.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250923.0.tgz", - "integrity": "sha512-avGZgJe3Vug0ff8oq5Hpa//x0dF9b12jKhDKqaEZaWl7mVGQk+GaA9lrO8TyJxzlfPIr/rXdvcRYJi/hbdgIJw==", + "version": "1.20251001.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20251001.0.tgz", + "integrity": "sha512-oT/K4YWNhmwpVmGeaHNmF7mLRfgjszlVr7lJtpS4jx5khmxmMzWZEEQRrJEpgzeHP6DOq9qWLPNT0bjMK7TchQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -8204,28 +8204,28 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20250923.0", - "@cloudflare/workerd-darwin-arm64": "1.20250923.0", - "@cloudflare/workerd-linux-64": "1.20250923.0", - "@cloudflare/workerd-linux-arm64": "1.20250923.0", - "@cloudflare/workerd-windows-64": "1.20250923.0" + "@cloudflare/workerd-darwin-64": "1.20251001.0", + "@cloudflare/workerd-darwin-arm64": "1.20251001.0", + "@cloudflare/workerd-linux-64": "1.20251001.0", + "@cloudflare/workerd-linux-arm64": "1.20251001.0", + "@cloudflare/workerd-windows-64": "1.20251001.0" } }, "node_modules/wrangler": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.39.0.tgz", - "integrity": "sha512-DP/iJdxXiLCCSshCwsMl7PjOTZNJq5vzeN/hgEHHU7m85GIjPCLH0VvR/T2IFO5twyd4CukPveM4blhVUmRy2A==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.41.0.tgz", + "integrity": "sha512-SPiBk/5SgCSIdcWw8EXc8DzqtrjbIU+/n22fQjyz4RnULAqCFJjy84F5crcWnb1J/iPiOzm7mS9bMGFFtpwS/w==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", - "@cloudflare/unenv-preset": "2.7.4", + "@cloudflare/unenv-preset": "2.7.5", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", - "miniflare": "4.20250923.0", + "miniflare": "4.20251001.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", - "workerd": "1.20250923.0" + "workerd": "1.20251001.0" }, "bin": { "wrangler": "bin/wrangler.js", @@ -8238,7 +8238,7 @@ "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250923.0" + "@cloudflare/workers-types": "^4.20251001.0" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { diff --git a/package.json b/package.json index 6286687..dd43f7e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,6 @@ "tailwindcss": "^4.1.13" }, "devDependencies": { - "wrangler": "^4.39.0" + "wrangler": "^4.41.0" } } diff --git a/src/functions/scheduled.ts b/src/functions/scheduled.ts index ae43469..f92d556 100644 --- a/src/functions/scheduled.ts +++ b/src/functions/scheduled.ts @@ -1,51 +1,97 @@ // Cloudflare Workers scheduled handler for cron jobs import type { ScheduledController, ScheduledEvent } from '@cloudflare/workers-types'; +import { DateTime } from 'luxon'; interface Env { - BASE_URL: string; - AUTH_USER: string; - AUTH_PASS: string; IMGS_BUCKET: R2Bucket; } +interface RadarImage { + id: string; + forecast_time: string; + modified: string; + created: string; + file_name: string; + type: string; +} + +interface RadarResponse { + data: { + types: { + IMSRadar: RadarImage[]; + }; + }; +} + class GeshemUpdate { private bucket: R2Bucket; - private env: Env; + private static readonly API_URL = 'https://ims.gov.il/he/radar_satellite'; + private static readonly BASE_URL = 'https://ims.gov.il'; - constructor(bucket: R2Bucket, env: Env) { + constructor(bucket: R2Bucket) { this.bucket = bucket; - this.env = env; } - private async authenticate(request: Request): Promise<Request> { - const headers = new Headers(request.headers); - const credentials = btoa(`${this.env.AUTH_USER}:${this.env.AUTH_PASS}`); - headers.set('Authorization', `Basic ${credentials}`); + private convertIsraeliToUTC(dateStr: string, timeStr: string): { date: string; time: string } { + // Parse YYYYMMDD HHMM format in Israeli timezone + const israeliTime = DateTime.fromFormat( + `${dateStr} ${timeStr}`, + 'yyyyMMdd HHmm', + { zone: 'Asia/Jerusalem' } + ); - return new Request(request.url, { - method: request.method, - headers, - body: request.body - }); + // Convert to UTC + const utcTime = israeliTime.toUTC(); + + // Format as YYYYMMDD and HHMM + return { + date: utcTime.toFormat('yyyyMMdd'), + time: utcTime.toFormat('HHmm') + }; } - private async getLatestImages(): Promise<string[]> { - const authenticatedRequest = await this.authenticate(new Request(this.env.BASE_URL)); - const response = await fetch(authenticatedRequest); - const html = await response.text(); + private async getLatestImage(): Promise<RadarImage | null> { + const response = await fetch(GeshemUpdate.API_URL); - const regex = /radar280comp_\d+\.png/g; - const matches = html.match(regex) || []; - const uniqueImages = [...new Set(matches)]; + if (!response.ok) { + throw new Error(`Failed to fetch radar data: ${response.statusText}`); + } + + const data: RadarResponse = await response.json(); + const images = data.data.types.IMSRadar; + + if (!images || images.length === 0) { + return null; + } - return uniqueImages.sort().slice(-10); + // Get the latest image (last in the array) + return images[images.length - 1]; } - private async getLatestBucketKeys(): Promise<string[]> { - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - const yesterdayStr = yesterday.toISOString().slice(0, 10).replace(/-/g, ''); + private parseFilename(filepath: string): { date: string; time: string } | null { + // Extract filename from path: /sites/default/files/ims_data/map_images/IMSRadar4GIS/IMSRadar4GIS_202510030935_0.png + const filename = filepath.split('/').pop(); + if (!filename) return null; + + // Match pattern: IMSRadar4GIS_YYYYMMDDHHMM_0.png + const match = filename.match(/IMSRadar4GIS_(\d{8})(\d{4})_\d\.png/); + if (!match) return null; + + const dateStr = match[1]; // YYYYMMDD + const timeStr = match[2]; // HHMM + + return { date: dateStr, time: timeStr }; + } + + private async getExistingGisKeys(): Promise<Set<string>> { + // Get yesterday's date in YYYYMMDD format + const yesterday = DateTime.utc().minus({ days: 1 }); + const yesterdayStr = yesterday.toFormat('yyyyMMdd'); + + console.log(`Listing bucket keys starting from ${yesterdayStr}`); + + const existingKeys = new Set<string>(); try { const objects = await this.bucket.list({ @@ -53,42 +99,70 @@ class GeshemUpdate { startAfter: `imgs/${yesterdayStr}` }); - return objects.objects.map(obj => obj.key); + for (const obj of objects.objects) { + // Only include keys that end with gis.png + if (obj.key.endsWith('gis.png')) { + existingKeys.add(obj.key); + } + } + + console.log(`Found ${existingKeys.size} existing gis.png files since ${yesterdayStr}`); } catch (error) { console.error('Error listing bucket objects:', error); - return []; } + + return existingKeys; } - private async fetchMissingImages(): Promise<boolean> { - const images = await this.getLatestImages(); - const s3Images = await this.getLatestBucketKeys(); - let updated = false; + private async fetchAndStoreImages(): Promise<number> { + // Get all images from API + const apiImage = await this.getLatestImage(); + if (!apiImage) { + console.log('No images found in API response'); + return 0; + } + + // Get existing gis.png keys in bucket + const existingKeys = await this.getExistingGisKeys(); - for (const img of images) { - const key = this.keyFromFilename(img); - if (!s3Images.includes(key)) { - await this.fetchImage(img, key); - updated = true; - } + let savedCount = 0; + + console.log(`Processing image: ${apiImage.file_name}`); + + // Parse filename to get date and time + const parsed = this.parseFilename(apiImage.file_name); + if (!parsed) { + console.error(`Failed to parse filename: ${apiImage.file_name}`); + return 0; } - return updated; - } + console.log(`Parsed Israeli time - Date: ${parsed.date}, Time: ${parsed.time}`); - private async fetchImage(imgName: string, key: string): Promise<void> { - console.log(`Downloading ${imgName} from web server`); + // Convert Israeli time to UTC + const utc = this.convertIsraeliToUTC(parsed.date, parsed.time); + console.log(`Converted to UTC - Date: ${utc.date}, Time: ${utc.time}`); - const imageUrl = `${this.env.BASE_URL}/${imgName}`; - const authenticatedRequest = await this.authenticate(new Request(imageUrl)); - const response = await fetch(authenticatedRequest); + // Build R2 key: imgs/YYYYMMDD/HHMM/gis.png + const key = `imgs/${utc.date}/${utc.time}/gis.png`; + + // Check if already exists + if (existingKeys.has(key)) { + console.log(`Image already exists at ${key}`); + return 0; + } + // Fetch the image + const imageUrl = `${GeshemUpdate.BASE_URL}${apiImage.file_name}`; + console.log(`Downloading from ${imageUrl}`); + + const response = await fetch(imageUrl); if (!response.ok) { throw new Error(`Failed to fetch image: ${response.statusText}`); } const imageData = await response.arrayBuffer(); + // Upload to R2 console.log(`Uploading to ${key}`); await this.bucket.put(key, imageData, { httpMetadata: { @@ -96,30 +170,17 @@ class GeshemUpdate { cacheControl: 'public, max-age=31536000' } }); - } - - private keyFromFilename(filename: string): string { - const [name, dateStr] = filename.split('.')[0].split('_'); - const res = 280; - // Parse date: YYYYMMDDHHMM - const year = dateStr.slice(0, 4); - const month = dateStr.slice(4, 6); - const day = dateStr.slice(6, 8); - const hour = dateStr.slice(8, 10); - const minute = dateStr.slice(10, 12); + savedCount++; - const d = `${year}${month}${day}`; - const t = `${hour}${minute}`; - - return `imgs/${d}/${t}/${res}.png`; + return savedCount; } async run(): Promise<string> { try { - const updated = await this.fetchMissingImages(); - return `Updated: ${updated}`; + const savedCount = await this.fetchAndStoreImages(); + return `Saved ${savedCount} new images`; } catch (error) { console.error('Error in GeshemUpdate.run():', error); throw error; @@ -127,30 +188,34 @@ class GeshemUpdate { } } +export async function handleSchedule( + controller: ScheduledController, + env: Env, + ctx: ExecutionContext +): Promise<void> { + console.log('Cron job triggered at', new Date().toISOString()); + + try { + if (!env.IMGS_BUCKET) { + throw new Error('R2 bucket not configured'); + } + + const updater = new GeshemUpdate(env.IMGS_BUCKET); + const result = await updater.run(); + + console.log(`Cron job completed: ${result}`); + } catch (error) { + console.error('Error in scheduled function:', error); + throw error; + } +} + export default { async scheduled( controller: ScheduledController, env: Env, ctx: ExecutionContext ): Promise<void> { - console.log('Cron job triggered at', new Date().toISOString()); - - try { - if (!env?.BASE_URL || !env?.AUTH_USER || !env?.AUTH_PASS) { - throw new Error('Missing environment variables'); - } - - if (!env.IMGS_BUCKET) { - throw new Error('R2 bucket not configured'); - } - - const updater = new GeshemUpdate(env.IMGS_BUCKET, env); - const result = await updater.run(); - - console.log(`Cron job completed: ${result}`); - } catch (error) { - console.error('Error in scheduled function:', error); - throw error; - } + return handleSchedule(controller, env, ctx); } };
\ No newline at end of file diff --git a/src/worker.ts b/src/worker.ts new file mode 100644 index 0000000..e982634 --- /dev/null +++ b/src/worker.ts @@ -0,0 +1,18 @@ +import type { SSRManifest } from 'astro'; +import { App } from 'astro/app'; +import { handle } from '@astrojs/cloudflare/handler'; +import { handleSchedule } from './functions/scheduled'; + +export function createExports(manifest: SSRManifest) { + const app = new App(manifest); + return { + default: { + async fetch(request, env, ctx) { + return handle(manifest, app, request, env, ctx); + }, + async scheduled(controller, env, ctx) { + return await handleSchedule(controller, env, ctx); + } + } + }; +} diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 5d6133a..a7db243 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,6 +1,6 @@ /* eslint-disable */ // Generated by Wrangler by running `wrangler types` (hash: 86ac7c910eaa8ed87087e10bf93f5438) -// Runtime types generated with workerd@1.20250923.0 2025-09-24 nodejs_compat +// Runtime types generated with workerd@1.20251001.0 2025-09-24 nodejs_compat declare namespace Cloudflare { interface GlobalProps { mainModule: typeof import("./dist/_worker.js/index"); @@ -7465,6 +7465,19 @@ interface MediaError extends Error { readonly message: string; readonly stack?: string; } +declare module 'cloudflare:node' { + interface NodeStyleServer { + listen(...args: unknown[]): this; + address(): { + port?: number | null | undefined; + }; + } + export function httpServerHandler(port: number): ExportedHandler; + export function httpServerHandler(options: { + port: number; + }): ExportedHandler; + export function httpServerHandler(server: NodeStyleServer): ExportedHandler; +} type Params<P extends string = any> = Record<P, string | string[]>; type EventContext<Env, P extends string, Data> = { request: Request<unknown, IncomingRequestCfProperties<unknown>>; @@ -7722,19 +7735,6 @@ declare namespace Cloudflare { & (K extends GlobalProp<"durableNamespaces", never> ? MainModule[K] extends new (...args: any[]) => infer DoInstance ? DoInstance extends Rpc.DurableObjectBranded ? DurableObjectNamespace<DoInstance> : DurableObjectNamespace<undefined> : DurableObjectNamespace<undefined> : {}); }; } -declare module 'cloudflare:node' { - export interface DefaultHandler { - fetch?(request: Request): Response | Promise<Response>; - tail?(events: TraceItem[]): void | Promise<void>; - trace?(traces: TraceItem[]): void | Promise<void>; - scheduled?(controller: ScheduledController): void | Promise<void>; - queue?(batch: MessageBatch<unknown>): void | Promise<void>; - test?(controller: TestController): void | Promise<void>; - } - export function httpServerHandler(options: { - port: number; - }, handlers?: Omit<DefaultHandler, 'fetch'>): DefaultHandler; -} declare namespace CloudflareWorkersModule { export type RpcStub<T extends Rpc.Stubable> = Rpc.Stub<T>; export const RpcStub: { diff --git a/wrangler.jsonc b/wrangler.jsonc index db8f5e7..052b59e 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -27,8 +27,10 @@ "pattern": "www.geshem.space", "custom_domain": true } - ] - // "triggers": { - // "crons": ["* * * * *"] - // } + ], + "triggers": { + "crons": [ + "* * * * *" + ] + } }
\ No newline at end of file |
