From 47a520e9450e5b82435bd929e82cb148e17622c1 Mon Sep 17 00:00:00 2001 From: Mike Conrad Date: Wed, 21 May 2025 16:36:54 -0400 Subject: [PATCH] Fix formatting --- .gitignore | 1 + adonisrc.ts | 2 +- app/Helpers/Replays.ts | 28 ++++++++------- app/Helpers/Sentry.ts | 11 +++--- app/Helpers/Webhook.ts | 36 ++++++++++--------- app/controllers/replays_controller.ts | 37 ++++++++++--------- app/models/replay.ts | 40 +++++++++------------ compose.yml | 34 ++++++++++-------- config/redis.ts | 2 +- inertia/app/app.ts | 4 +-- inertia/pages/Replays/Index.vue | 52 ++++++++++++++------------- start/env.ts | 2 +- start/routes.ts | 5 ++- 13 files changed, 129 insertions(+), 125 deletions(-) diff --git a/.gitignore b/.gitignore index cf36d1b..a444a1d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ yarn-error.log # Platform specific .DS_Store +*compose-prod.yml diff --git a/adonisrc.ts b/adonisrc.ts index 9da686c..cbeac72 100644 --- a/adonisrc.ts +++ b/adonisrc.ts @@ -53,7 +53,7 @@ export default defineConfig({ () => import('@adonisjs/lucid/database_provider'), () => import('@adonisjs/auth/auth_provider'), () => import('@adonisjs/inertia/inertia_provider'), - () => import('@adonisjs/redis/redis_provider') + () => import('@adonisjs/redis/redis_provider'), ], /* diff --git a/app/Helpers/Replays.ts b/app/Helpers/Replays.ts index 8a14e70..54d69ab 100644 --- a/app/Helpers/Replays.ts +++ b/app/Helpers/Replays.ts @@ -1,29 +1,29 @@ import Replay from '#models/replay' -import {parseSentryLinkHeader, SentryPagination} from './Sentry.js' +import { parseSentryLinkHeader, SentryPagination } from './Sentry.js' import env from '#start/env' let recordsUpdated = 0 const SENTRY_TOKEN = env.get('SENTRY_TOKEN') interface ApiResponse { - data: T; + data: T // optionally, you can define `meta`, `errors`, etc. if your API returns them } export async function fetchBatch(url: string) { const options: RequestInit = { headers: { - Authorization: `Bearer ${SENTRY_TOKEN}` - } + Authorization: `Bearer ${SENTRY_TOKEN}`, + }, } const req = await fetch(url, options) if (!req.ok) { - throw new Error(`Request failed with status ${req.status}`); + throw new Error(`Request failed with status ${req.status}`) } - const resp = await req.json() as ApiResponse; - const replays = resp.data; + const resp = (await req.json()) as ApiResponse + const replays = resp.data const headers = req.headers - const cleanedData = replays.map(record => sanitizeInput(record, Replay.allowedFields)) + const cleanedData = replays.map((record) => sanitizeInput(record, Replay.allowedFields)) let updated = await Replay.updateOrCreateMany('id', cleanedData) recordsUpdated = recordsUpdated + updated.length @@ -39,12 +39,14 @@ export async function fetchBatch(url: string) { } console.log('no more results') return { recordsUpdated } - } function sanitizeInput(data: Record, allowedFields: string[]) { - return allowedFields.reduce((acc, key) => { - if (key in data) acc[key] = data[key] - return acc - }, {} as Record) + return allowedFields.reduce( + (acc, key) => { + if (key in data) acc[key] = data[key] + return acc + }, + {} as Record + ) } diff --git a/app/Helpers/Sentry.ts b/app/Helpers/Sentry.ts index cab1bda..461d6ee 100644 --- a/app/Helpers/Sentry.ts +++ b/app/Helpers/Sentry.ts @@ -1,12 +1,11 @@ - export interface SentryPagination { - previous: string; - hasPreviousResults: boolean; - hasNextResults: boolean; + previous: string + hasPreviousResults: boolean + hasNextResults: boolean next: string } export function parseSentryLinkHeader(header: string): SentryPagination { - const links = header.split(',').map(part => part.trim()) + const links = header.split(',').map((part) => part.trim()) let result = {} as SentryPagination for (const link of links) { @@ -25,4 +24,4 @@ export function parseSentryLinkHeader(header: string): SentryPagination { } return result -} \ No newline at end of file +} diff --git a/app/Helpers/Webhook.ts b/app/Helpers/Webhook.ts index 35477cc..4deb023 100644 --- a/app/Helpers/Webhook.ts +++ b/app/Helpers/Webhook.ts @@ -1,19 +1,21 @@ import env from '#start/env' -export async function sendDataToWebhook(responseData:{ version: number, updatedAt: Date, numberOfRecords: number, data: unknown}) { - try { - console.log('syncing to webhook') - await fetch(env.get('WEBHOOK_URL'), - { - headers: - { - 'content-type': 'application/json' - }, - method: 'POST', - body: JSON.stringify(responseData) - } - ) - } catch (e) { - console.error('error sending webhook data', e) - } -} \ No newline at end of file +export async function sendDataToWebhook(responseData: { + version: number + updatedAt: Date + numberOfRecords: number + data: unknown +}) { + try { + console.log('syncing to webhook') + await fetch(env.get('WEBHOOK_URL'), { + headers: { + 'content-type': 'application/json', + }, + method: 'POST', + body: JSON.stringify(responseData), + }) + } catch (e) { + console.error('error sending webhook data', e) + } +} diff --git a/app/controllers/replays_controller.ts b/app/controllers/replays_controller.ts index 1a7961f..8ee73c2 100644 --- a/app/controllers/replays_controller.ts +++ b/app/controllers/replays_controller.ts @@ -6,9 +6,7 @@ import redis from '@adonisjs/redis/services/main' import { fetchBatch } from '../Helpers/Replays.js' import { sendDataToWebhook } from '../Helpers/Webhook.js' - export default class ReplaysController { - public async stats({ request, response }: HttpContext) { const { sendToWebhook } = request.qs() const latestVersion = await redis.get(`replays:stats:latest_version`) @@ -24,7 +22,12 @@ export default class ReplaysController { } } - let responseData = { version: results.version, updatedAt: results.updatedAt, numberOfRecords: results.rows.length, data: results.rows } + let responseData = { + version: results.version, + updatedAt: results.updatedAt, + numberOfRecords: results.rows.length, + data: results.rows, + } if (sendToWebhook) { await sendDataToWebhook(responseData) } @@ -40,7 +43,7 @@ export default class ReplaysController { let paginated, meta, replays if (data) { - ({ paginated, meta, replays } = JSON.parse(data)) + ;({ paginated, meta, replays } = JSON.parse(data)) } else { paginated = await Replay.query().paginate(page, perPage) paginated.baseUrl('/list') @@ -49,7 +52,7 @@ export default class ReplaysController { meta = { ...json.meta, - links: buildPaginationLinks(json.meta) + links: buildPaginationLinks(json.meta), } replays = json.data @@ -60,17 +63,14 @@ export default class ReplaysController { return inertia.render('Replays/Index', { data: { replays, - meta - } + meta, + }, }) - - } async index({ request, response }: HttpContext) { const { statsPeriod, start, end } = request.qs() - - let queryString: string = '?statsPeriod=24h'// Default in case none is provided + let queryString: string = '?statsPeriod=24h' // Default in case none is provided if (statsPeriod) { queryString = `?statsPeriod=${statsPeriod}` } else if (start && end) { @@ -82,25 +82,28 @@ export default class ReplaysController { return response.json({ version: queryResults.latestVersion, ...queryResults }) } - } - -function buildPaginationLinks(meta: { previousPageUrl: string, lastPage: number; currentPage: number; nextPageUrl: string }) { +function buildPaginationLinks(meta: { + previousPageUrl: string + lastPage: number + currentPage: number + nextPageUrl: string +}) { const links = [] // Previous links.push({ url: meta.previousPageUrl, label: '« Prev', - active: false + active: false, }) for (let page = 1; page <= meta.lastPage; page++) { links.push({ url: `/list?page=${page}`, label: page.toString(), - active: page === meta.currentPage + active: page === meta.currentPage, }) } @@ -108,7 +111,7 @@ function buildPaginationLinks(meta: { previousPageUrl: string, lastPage: number; links.push({ url: meta.nextPageUrl, label: 'Next »', - active: false + active: false, }) return links diff --git a/app/models/replay.ts b/app/models/replay.ts index e47a5c7..16b804f 100644 --- a/app/models/replay.ts +++ b/app/models/replay.ts @@ -5,7 +5,7 @@ import redis from '@adonisjs/redis/services/main' export default class Replay extends BaseModel { public static async updateReplayStats() { - let results = await db.rawQuery(` + let results = await db.rawQuery(` SELECT u.display_name, u.sessions, @@ -54,13 +54,12 @@ export default class Replay extends BaseModel { ) r ON true ORDER BY - u.total_time_seconds DESC;` - ) - const updatedVersion = await redis.incr('replays:stats:latest_version') - results.version = updatedVersion - results.updatedAt = Date.now() - await redis.set(`replays:stats:version:${updatedVersion}:results`, JSON.stringify(results)) - return results + u.total_time_seconds DESC;`) + const updatedVersion = await redis.incr('replays:stats:latest_version') + results.version = updatedVersion + results.updatedAt = Date.now() + await redis.set(`replays:stats:version:${updatedVersion}:results`, JSON.stringify(results)) + return results } @column({ isPrimary: true }) declare id: string @@ -72,14 +71,14 @@ export default class Replay extends BaseModel { prepare: (value) => { // The values from sentry are just arrays so convert them to json return JSON.stringify(value) - } + }, }) declare trace_ids: string[] @column({ prepare: (value) => { return JSON.stringify(value) - } + }, }) declare error_ids: string[] @@ -90,50 +89,44 @@ export default class Replay extends BaseModel { prepare: (value) => { // The values from sentry are just arrays so convert them to json return JSON.stringify(value) - } + }, }) declare tags: string[] @column() declare user: string[] - @column() declare sdk: any - @column() declare os: any - @column() declare browser: any - @column() declare device: any - + @column() declare ota_updates: any @column() declare is_archived: boolean | null - @column({ prepare: (value) => { // The values from sentry are just arrays so convert them to json return JSON.stringify(value) - } + }, }) - declare urls: any - + declare urls: any @column({ prepare: (value) => { // The values from sentry are just arrays so convert them to json return JSON.stringify(value) - } + }, }) declare clicks: any @@ -152,7 +145,7 @@ export default class Replay extends BaseModel { @column.dateTime() declare finished_at: DateTime | null - @column.dateTime({serializeAs: 'started_at'}) + @column.dateTime({ serializeAs: 'started_at' }) declare started_at: DateTime | null @column() @@ -170,12 +163,11 @@ export default class Replay extends BaseModel { @column() declare platform: string | null - @column({ prepare: (value) => { // The values from sentry are just arrays so convert them to json return JSON.stringify(value) - } + }, }) declare releases: any diff --git a/compose.yml b/compose.yml index f3b8c18..a0a4b29 100644 --- a/compose.yml +++ b/compose.yml @@ -13,12 +13,12 @@ services: - traefik scraper: labels: - - "traefik.enable=true" - - "traefik.docker.network=sentry_traefik" - - "traefik.http.routers.scraper.rule=Host(`sentry.docker.localhost`)" - - "traefik.http.services.scraper.loadbalancer.server.port=3333" - - "traefik.http.routers.scraper.entrypoints=http" - - "traefik.http.routers.scraper.service=scraper" + - 'traefik.enable=true' + - 'traefik.docker.network=sentry_traefik' + - 'traefik.http.routers.scraper.rule=Host(`sentry.docker.localhost`)' + - 'traefik.http.services.scraper.loadbalancer.server.port=3333' + - 'traefik.http.routers.scraper.entrypoints=http' + - 'traefik.http.routers.scraper.service=scraper' networks: - traefik - redis @@ -31,7 +31,7 @@ services: environment: - POSTGRES_PASSWORD=password healthcheck: - test: ["CMD-SHELL", "pg_isready", "-d", "postgres"] + test: ['CMD-SHELL', 'pg_isready', '-d', 'postgres'] interval: 5s timeout: 60s retries: 5 @@ -43,17 +43,21 @@ services: grafana: image: grafana/grafana:latest labels: - - "traefik.enable=true" - - "traefik.docker.network=sentry_traefik" - - "traefik.http.routers.grafana.rule=Host(`grafana.docker.localhost`)" - - "traefik.http.routers.grafana.entrypoints=http" - - "traefik.http.services.grafana.loadbalancer.server.port=3000" - - "traefik.http.routers.grafana.service=grafana" + - 'traefik.enable=true' + - 'traefik.docker.network=sentry_traefik' + - 'traefik.http.routers.grafana.rule=Host(`grafana.docker.localhost`)' + - 'traefik.http.routers.grafana.entrypoints=http' + - 'traefik.http.services.grafana.loadbalancer.server.port=3000' + - 'traefik.http.routers.grafana.service=grafana' networks: - traefik - database healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1"] + test: + [ + 'CMD-SHELL', + 'wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1', + ] interval: 10s timeout: 30s retries: 5 @@ -70,4 +74,4 @@ networks: redis: driver: bridge volumes: - pg_data: {} \ No newline at end of file + pg_data: {} diff --git a/config/redis.ts b/config/redis.ts index 49b59d9..543b9df 100644 --- a/config/redis.ts +++ b/config/redis.ts @@ -33,4 +33,4 @@ export default redisConfig declare module '@adonisjs/redis/types' { export interface RedisConnections extends InferConnections {} -} \ No newline at end of file +} diff --git a/inertia/app/app.ts b/inertia/app/app.ts index a65926c..2ff84ef 100644 --- a/inertia/app/app.ts +++ b/inertia/app/app.ts @@ -4,10 +4,10 @@ import '../css/app.css' import { createSSRApp, h } from 'vue' import type { DefineComponent } from 'vue' -import { createInertiaApp } from '@inertiajs/vue3' +import { createInertiaApp, Link } from '@inertiajs/vue3' import { resolvePageComponent } from '@adonisjs/inertia/helpers' - const appName = import.meta.env.VITE_APP_NAME || 'AdonisJS' +Vue.component('inertia-link', Link) createInertiaApp({ progress: { color: '#5468FF' }, diff --git a/inertia/pages/Replays/Index.vue b/inertia/pages/Replays/Index.vue index 4d0e358..0f19b74 100644 --- a/inertia/pages/Replays/Index.vue +++ b/inertia/pages/Replays/Index.vue @@ -9,7 +9,6 @@ Email Date Location - @@ -17,28 +16,33 @@ {{ replay.id }} {{ replay.user.email ?? replay.user.display_name }} {{ replay.finished_at }} - {{ replay.user.geo ? `${replay.user.geo.city} ${replay.user.geo.subdivision}, ${replay.user.geo.region}` : 'unknown' }} + + {{ + replay.user.geo + ? `${replay.user.geo.city} ${replay.user.geo.subdivision}, ${replay.user.geo.region}` + : 'unknown' + }} + -
+
- « First - + ‹ Prev @@ -48,9 +52,9 @@ :is="link.url ? Link : 'span'" :href="link.url" class="px-3 py-1 border rounded text-sm" - :class="{ - 'font-bold bg-gray-300': link.active, - 'text-gray-400 cursor-not-allowed': !link.url + :class="{ + 'font-bold bg-gray-300': link.active, + 'text-gray-400 cursor-not-allowed': !link.url, }" > @@ -58,18 +62,14 @@ - + Next › - Last » @@ -83,12 +83,12 @@ import { computed } from 'vue' import { Link } from '@inertiajs/vue3' const props = defineProps({ - data: Object + data: Object, }) // Core pagination values const links = computed(() => props.data.meta.links || []) -const currentIndex = computed(() => links.value.findIndex(link => link.active)) +const currentIndex = computed(() => links.value.findIndex((link) => link.active)) const maxVisible = 10 const half = Math.floor(maxVisible / 2) @@ -115,5 +115,7 @@ const nextPageUrl = computed(() => links.value[currentIndex.value + 1]?.url) const lastPageUrl = computed(() => links.value[links.value.length - 2]?.url) // last item is "Next »", second-last is last numbered const isFirstPage = computed(() => links.value[currentIndex.value]?.label === '1') -const isLastPage = computed(() => links.value[currentIndex.value]?.label === props.data.meta.last_page) +const isLastPage = computed( + () => links.value[currentIndex.value]?.label === props.data.meta.last_page +) diff --git a/start/env.ts b/start/env.ts index 54fa2df..9725ee4 100644 --- a/start/env.ts +++ b/start/env.ts @@ -34,5 +34,5 @@ export default await Env.create(new URL('../', import.meta.url), { PG_USER: Env.schema.string(), PG_PASSWORD: Env.schema.string(), - WEBHOOK_URL: Env.schema.string() + WEBHOOK_URL: Env.schema.string(), }) diff --git a/start/routes.ts b/start/routes.ts index 0baa600..a24fec2 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -11,6 +11,5 @@ import ReplaysController from '#controllers/replays_controller' import router from '@adonisjs/core/services/router' router.on('/').renderInertia('home') router.get('/replays', [ReplaysController, 'index']) -router.get('/list', [ReplaysController, 'list' -]) -router.get('/stats', [ReplaysController, 'stats']) \ No newline at end of file +router.get('/list', [ReplaysController, 'list']) +router.get('/stats', [ReplaysController, 'stats'])