Skip to content

Commit

Permalink
new telemetry system
Browse files Browse the repository at this point in the history
  • Loading branch information
0x7d8 committed Dec 12, 2024
1 parent c1995ec commit 1d9463a
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 32 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api",
"version": "1.0.1",
"version": "1.1.0",
"scripts": {
"build": "rm -rf lib && esbuild `find src \\( -name '*.ts' -o -name '*.tsx' \\)` --platform='node' --sourcemap --ignore-annotations --format='cjs' --target='es2022' --outdir='lib' && esbuild src/index.ts --platform='node' --sourcemap --ignore-annotations --format='cjs' --target='es2022' --outdir='lib' --banner:js='require(\"module-alias\").addAlias(\"@\", __dirname);'",
"kit": "drizzle-kit",
Expand Down
9 changes: 0 additions & 9 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,6 @@ server.path('/', (path) => path
)
.http('GET', '/send/{panel}/{data}', (http) => http
.onRequest((ctr) => {
const panel = ctr.params.get('panel', ''),
data = ctr.params.get('data', '')

const [ id, version ] = panel.split('@')
if (!id || id.length !== 23 || !allowedId.test(id)) return ctr.status(ctr.$status.NOT_FOUND).print({ errors: ['Invalid Panel ID'] })
if (!version || ctr["@"].github().history.indexOf(version) === -1) return ctr.status(ctr.$status.NOT_FOUND).print({ errors: ['Invalid Panel Version'] })

ctr["@"].telemetry.log(id, version, data, ctr.client.ip)

return ctr.print({})
})
)
Expand Down
13 changes: 13 additions & 0 deletions src/api/routes/global/telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { globalAPIRouter } from "@/api"

export = new globalAPIRouter.Path('/')
.http('POST', '/', (http) => http
.onRequest(async(ctr) => {
const [ data, error ] = await ctr.bindBody(ctr["@"].telemetry.telemetrySchema)
if (!data) return ctr.status(ctr.$status.BAD_REQUEST).print({ errors: error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`) })

ctr["@"].telemetry.log(ctr.client.ip, data)

return ctr.print({})
})
)
69 changes: 53 additions & 16 deletions src/globals/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,73 @@
import database from "@/globals/database"
import logger from "@/globals/logger"
import { network, string, time } from "@rjweb/utils"
import { network, object, string, time } from "@rjweb/utils"
import * as schema from "@/schema"
import { lookup } from "@/globals/ip"
import cache from "@/globals/cache"
import { z } from "zod"
import github from "@/globals/github"

export type Telemetry = {
export const telemetrySchema = z.object({
id: z.string().uuid(),
telemetry_version: z.literal(1),

blueprint: z.object({
version: z.string().refine((str) => github().history.includes(str)),
extensions: z.object({
identifier: z.string(),
version: z.string(),
target: z.string()
}).array()
}),

panel: z.object({
version: z.string(),
phpVersion: z.string(),

drivers: z.object({
backup: z.object({
type: z.string()
}),

cache: z.object({
type: z.string()
}),

database: z.object({
type: z.string(),
version: z.string()
})
})
})
})

export type Telemetry = {
panelId: string
version: string
data: string
telemetryVersion: number
ip: string

continent: string | null
country: string | null
data: Pick<z.infer<typeof telemetrySchema>, 'blueprint' | 'panel'>
}

const processing: Telemetry[] = []

/**
* Log a new Telemetry
* @since 1.0.0
*/ export function log(panelId: string, version: string, data: string, ip: network.IPAddress): Telemetry {
const telemetry: Telemetry = {
panelId, version, data,
*/ export function log(ip: network.IPAddress, telemetry: z.infer<typeof telemetrySchema>): Telemetry {
const data: Telemetry = {
panelId: telemetry.id,
telemetryVersion: telemetry.telemetry_version,
ip: ip.usual(),
continent: null,
country: null
country: null,
data: object.pick(telemetry, ['blueprint', 'panel'])
}

processing.push(telemetry)
processing.push(data)

return telemetry
return data
}

const process = async(): Promise<void> => {
Expand All @@ -46,22 +83,22 @@ const process = async(): Promise<void> => {
t.continent = ip.continent
t.country = ip.country
}

t.ip = string.hash(t.ip, { algorithm: 'sha256 '})
}

const panels = new Set(telemetry.map((t) => t.panelId))

await Promise.all(Array.from(panels).map((id) => cache.use(`panel:${id}`, () => database.write.insert(schema.telemetryPanels)
.values({ id, version: telemetry.find((t) => t.panelId === id)!.version })
.values({ id })
.onConflictDoUpdate({
target: schema.telemetryPanels.id,
set: {
version: telemetry.find((t) => t.panelId === id)!.version
}
set: { lastUpdate: new Date() }
})
)))

await database.write.insert(schema.telemetryData)
.values(telemetry.map((t) => Object.assign(t, { ip: string.hash(t.ip, { algorithm: 'sha256' }) })))
.values(telemetry)
.onConflictDoNothing()
} catch (err) {
processing.push(...telemetry)
Expand Down
13 changes: 7 additions & 6 deletions src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { sql } from "drizzle-orm"
import { integer, pgTable, varchar, uniqueIndex, pgEnum, serial, timestamp, jsonb, text, boolean, primaryKey, char, index } from "drizzle-orm/pg-core"
import { integer, pgTable, varchar, uniqueIndex, pgEnum, serial, timestamp, jsonb, text, boolean, primaryKey, char, index, uuid, smallint } from "drizzle-orm/pg-core"

export const platforms = Object.freeze(['SOURCEXCHANGE', 'BUILTBYBIT', 'GITHUB'] as const)
export const currency = Object.freeze(['USD', 'EUR'] as const)
Expand All @@ -10,17 +10,18 @@ export type Currency = typeof currency[number]
export const extensionType = pgEnum('extension_type', ['THEME', 'EXTENSION'])

export const telemetryPanels = pgTable('telemetry_panels', {
id: char('id', { length: 23 }).primaryKey().notNull(),
version: varchar('version', { length: 31 }).notNull(),
id: uuid('id').primaryKey(),

created: timestamp('created').notNull().default(sql`now()`)
created: timestamp('created').notNull().default(sql`now()`),
lastUpdate: timestamp('last_update').notNull().default(sql`now()`)
})

export const telemetryData = pgTable('telemetry_data', {
id: serial('id').primaryKey().notNull(),
panelId: char('panel_id', { length: 23 }).notNull().references(() => telemetryPanels.id),
panelId: uuid('panel_id').notNull().references(() => telemetryPanels.id),

data: varchar('data', { length: 255 }).notNull(),
telemetryVersion: smallint('telemetry_version').notNull(),
data: jsonb('data').notNull(),
ip: char('ip', { length: 64 }).notNull(),

continent: char('continent', { length: 2 }),
Expand Down

0 comments on commit 1d9463a

Please sign in to comment.