feat:移除了弹窗,服务器添加sls

This commit is contained in:
2026-09-08 22:39:45 +08:00
commit 6a295f9a7a
4082 changed files with 1322534 additions and 0 deletions

View File

@ -0,0 +1,13 @@
import { applyD1Migrations, env, type D1Migration } from 'cloudflare:test'
import { beforeAll } from 'vitest'
declare module 'cloudflare:test' {
interface ProvidedEnv {
DB: D1Database
TEST_MIGRATIONS: D1Migration[]
}
}
beforeAll(async () => {
await applyD1Migrations(env.DB, env.TEST_MIGRATIONS)
})

View File

@ -0,0 +1,210 @@
import { env, SELF } from 'cloudflare:test'
import { describe, expect, it } from 'vitest'
import { runMaintenance, type Bindings } from '../src'
import { batchSchema } from '../src/schema'
declare module 'cloudflare:test' {
interface ProvidedEnv extends Bindings {}
}
const installationId = '018f6ee8-4cb1-7db3-8a8d-8df96f122d85'
function batch(
batchId: string,
events: Array<Record<string, unknown>>,
clientInstallationId = installationId,
): Record<string, unknown> {
return {
schema_version: 1,
batch_id: batchId,
installation_id: clientInstallationId,
app: {
version: '1.7.1',
environment: 'production',
platform: 'windows',
arch: 'x86_64',
},
events,
}
}
function heartbeat(eventId: string, day = new Date().toISOString().slice(0, 10)) {
return { type: 'heartbeat', event_id: eventId, occurred_at: `${day}T12:00:00.000Z`, day }
}
async function post(payload: unknown): Promise<Response> {
return await SELF.fetch('https://telemetry.example/v1/batch', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(payload),
})
}
describe('telemetry worker', () => {
it('serves health without storage access', async () => {
const response = await SELF.fetch('https://telemetry.example/health')
expect(response.status).toBe(200)
expect(await response.json()).toEqual({ status: 'ok', schema_version: 1 })
})
it('accepts strict heartbeat batches and rejects unknown structures', async () => {
const invalid = batch('11111111-1111-4111-8111-111111111111', [
{ ...heartbeat('21111111-1111-4111-8111-111111111111'), unknown: true },
])
expect(batchSchema.safeParse(invalid).success).toBe(false)
expect((await post(invalid)).status).toBe(400)
const oversized = await SELF.fetch('https://telemetry.example/v1/batch', {
method: 'POST',
body: 'x'.repeat(65 * 1024),
})
expect(oversized.status).toBe(413)
})
it('rejects legacy error batches without charging quota or storing payloads', async () => {
const payload = batch('12111111-1111-4111-8111-111111111111', [
{
type: 'error',
event_id: '13111111-1111-4111-8111-111111111111',
occurred_at: new Date().toISOString(),
fingerprint: 'a'.repeat(64),
occurrence_count: 1,
error_type: 'legacy_error',
message: 'must not be retained',
},
])
const response = await post(payload)
expect(response.status).toBe(400)
expect(await response.json()).toEqual({ error: 'error_reporting_disabled' })
const stored = await env.DB.prepare(
'SELECT COUNT(*) AS count FROM accepted_batches WHERE batch_id = ?',
)
.bind('12111111-1111-4111-8111-111111111111')
.first<{ count: number }>()
expect(stored?.count).toBe(0)
})
it('enforces the daily installation batch cap without charging duplicates', async () => {
const cappedInstallation = '018f6ee8-4cb1-7db3-8a8d-8df96f122d99'
const day = new Date().toISOString().slice(0, 10)
const payloadFor = (index: number) =>
batch(
`60000000-0000-4000-8000-${index.toString().padStart(12, '0')}`,
[heartbeat(`70000000-0000-4000-8000-${index.toString().padStart(12, '0')}`, day)],
cappedInstallation,
)
for (let index = 0; index < 25; index++)
expect((await post(payloadFor(index))).status).toBe(200)
expect((await post(payloadFor(0))).status).toBe(200)
const rejected = await post(payloadFor(25))
expect(rejected.status).toBe(429)
expect(rejected.headers.get('Retry-After')).not.toBeNull()
})
it('opens the global ingestion circuit breaker at 100,000 accepted batches', async () => {
const day = new Date().toISOString().slice(0, 10)
await env.DB.prepare(
`INSERT INTO ingestion_global_daily (day, accepted_batches)
VALUES (?, 99999)
ON CONFLICT (day) DO UPDATE SET accepted_batches = 99999`,
)
.bind(day)
.run()
const first = batch(
'80000000-0000-4000-8000-000000000001',
[heartbeat('81000000-0000-4000-8000-000000000001', day)],
'018f6ee8-4cb1-7db3-8a8d-8df96f122d98',
)
expect((await post(first)).status).toBe(200)
const rejected = await post({
...first,
batch_id: '80000000-0000-4000-8000-000000000002',
events: [heartbeat('81000000-0000-4000-8000-000000000002', day)],
})
expect(rejected.status).toBe(429)
})
it('hashes installations and keeps heartbeat batches idempotent', async () => {
const payload = batch('31111111-1111-4111-8111-111111111111', [
heartbeat('41111111-1111-4111-8111-111111111111', '2026-08-14'),
])
expect((await post(payload)).status).toBe(200)
expect((await post(payload)).status).toBe(200)
const installation = await env.DB.prepare('SELECT installation_hash FROM installations').first<{
installation_hash: string
}>()
expect(installation?.installation_hash).toMatch(/^[0-9a-f]{64}$/)
expect(installation?.installation_hash).not.toBe(installationId)
const active = await env.DB.prepare('SELECT COUNT(*) AS count FROM daily_active').first<{
count: number
}>()
expect(active?.count).toBe(1)
})
it('keeps offline heartbeat dates for DAU, WAU, and MAU queries', async () => {
const day = (daysAgo: number) =>
new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1_000).toISOString().slice(0, 10)
const samples = [
[
'32111111-1111-4111-8111-111111111111',
'42111111-1111-4111-8111-111111111111',
installationId.replace(/85$/, '81'),
0,
],
[
'33111111-1111-4111-8111-111111111111',
'43111111-1111-4111-8111-111111111111',
installationId.replace(/85$/, '82'),
6,
],
[
'34111111-1111-4111-8111-111111111111',
'44111111-1111-4111-8111-111111111111',
installationId.replace(/85$/, '83'),
29,
],
] as const
for (const [batchId, eventId, clientId, offset] of samples) {
const heartbeatDay = day(offset)
expect(
(await post(batch(batchId, [heartbeat(eventId, heartbeatDay)], clientId))).status,
).toBe(200)
}
const counts = await env.DB.prepare(
`SELECT
COUNT(DISTINCT CASE WHEN day = ? THEN installation_hash END) AS dau,
COUNT(DISTINCT CASE WHEN day >= date(?, '-6 days') THEN installation_hash END) AS wau,
COUNT(DISTINCT CASE WHEN day >= date(?, '-29 days') THEN installation_hash END) AS mau
FROM daily_active`,
)
.bind(day(0), day(0), day(0))
.first<{ dau: number; wau: number; mau: number }>()
expect(counts).toEqual({ dau: 1, wau: 2, mau: 3 })
})
it('refreshes usage totals without rebuilding error aggregates', async () => {
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1_000).toISOString().slice(0, 10)
const response = await post(
batch('a2111111-1111-4111-8111-111111111111', [
heartbeat('a3111111-1111-4111-8111-111111111111', yesterday),
]),
)
expect(response.status).toBe(200)
await runMaintenance(env.DB)
const totals = await env.DB.prepare(
'SELECT new_installations, active_installations, error_occurrences, distinct_error_groups FROM daily_totals WHERE day = ?',
)
.bind(yesterday)
.first<Record<string, number>>()
expect(totals?.active_installations).toBeGreaterThanOrEqual(1)
expect(totals?.error_occurrences).toBe(0)
expect(totals?.distinct_error_groups).toBe(0)
})
})