diff --git a/.adonisjs/client/data.d.ts b/.adonisjs/client/data.d.ts index 35aaacc..4208617 100644 --- a/.adonisjs/client/data.d.ts +++ b/.adonisjs/client/data.d.ts @@ -6,7 +6,6 @@ /// import type { InferData, InferVariants } from '@adonisjs/core/types/transformers' import type { InferSharedProps, InferFlashData } from '@adonisjs/inertia/types' -import type KitsBuildLogTransformer from '#app/kits/transformers/build_log_transformer' import type KitsKitRecordTransformer from '#app/kits/transformers/kit_record_transformer' import type KitsKitTransformer from '#app/kits/transformers/kit_transformer' import type ProfileProfileTransformer from '#app/profile/transformers/profile_transformer' @@ -14,10 +13,6 @@ import type InertiaMiddleware from '#core/middleware/inertia_middleware' export namespace Data { export namespace Kits { - export type BuildLog = InferData - export namespace BuildLog { - export type Variants = InferVariants - } export type KitRecord = InferData export namespace KitRecord { export type Variants = InferVariants diff --git a/app/auth/controllers/callback_controller.ts b/app/auth/controllers/callback_controller.ts index a5261eb..78b7acc 100644 --- a/app/auth/controllers/callback_controller.ts +++ b/app/auth/controllers/callback_controller.ts @@ -31,9 +31,9 @@ export default class CallbackController { } else { this.repoSyncService .syncUser(did) - .then(({ handle, kitsSynced, logsSynced }) => { + .then(({ handle, kitsSynced }) => { logger.info( - { did, handle, kitsSynced, logsSynced }, + { did, handle, kitsSynced }, `PDS sync completed on login for ${handle}` ) }) diff --git a/app/explore/services/explore_service.ts b/app/explore/services/explore_service.ts index a7a70a2..086787c 100644 --- a/app/explore/services/explore_service.ts +++ b/app/explore/services/explore_service.ts @@ -9,11 +9,7 @@ export class ExploreService { async listKits(params: { q?: string; page: number }): Promise> { const page = Math.max(1, params.page) - let query = Kit.query() - .where('status', 'done') - .preload('profile') - .preload('buildLogs') - .orderBy('indexed_at', 'desc') + let query = Kit.query().where('status', 'done').preload('profile').orderBy('indexed_at', 'desc') if (params.q) { const term = params.q.toLowerCase().trim() diff --git a/app/kits/models/build_log.ts b/app/kits/models/build_log.ts deleted file mode 100644 index 24c482f..0000000 --- a/app/kits/models/build_log.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm' -import type { BelongsTo } from '@adonisjs/lucid/types/relations' -import { DateTime } from 'luxon' -import Kit from './kit.js' -import Profile from '#profile/models/profile' - -export default class BuildLog extends BaseModel { - static table = 'build_logs' - - @column({ isPrimary: true }) - declare id: number - - @column() - declare uri: string - - @column() - declare cid: string | null - - @column() - declare did: string - - @column() - declare kitUri: string - - @belongsTo(() => Kit, { foreignKey: 'kitUri', localKey: 'uri' }) - declare kit: BelongsTo - - @belongsTo(() => Profile, { foreignKey: 'did', localKey: 'did' }) - declare profile: BelongsTo - - @column() - declare stage: string | null - - @column() - declare text: string | null - - @column.dateTime() - declare recordCreatedAt: DateTime | null - - @column.dateTime() - declare indexedAt: DateTime - - @column.dateTime({ autoCreate: true }) - declare createdAt: DateTime - - @column.dateTime({ autoCreate: true, autoUpdate: true }) - declare updatedAt: DateTime -} diff --git a/app/kits/models/kit.ts b/app/kits/models/kit.ts index db515b1..2764633 100644 --- a/app/kits/models/kit.ts +++ b/app/kits/models/kit.ts @@ -1,7 +1,6 @@ -import { BaseModel, belongsTo, column, hasMany } from '@adonisjs/lucid/orm' -import type { BelongsTo, HasMany } from '@adonisjs/lucid/types/relations' +import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm' +import type { BelongsTo } from '@adonisjs/lucid/types/relations' import { DateTime } from 'luxon' -import BuildLog from './build_log.js' import Profile from '#profile/models/profile' export default class Kit extends BaseModel { @@ -70,9 +69,6 @@ export default class Kit extends BaseModel { @belongsTo(() => Profile, { foreignKey: 'did', localKey: 'did' }) declare profile: BelongsTo - @hasMany(() => BuildLog, { localKey: 'uri', foreignKey: 'kitUri' }) - declare buildLogs: HasMany - static async hasKitsFor(did: string): Promise { const result = await Kit.query().where('did', did).count('* as total').first() return Number(result?.$extras?.total ?? 0) > 0 diff --git a/app/kits/services/repo_sync_service.ts b/app/kits/services/repo_sync_service.ts index 54850eb..759a273 100644 --- a/app/kits/services/repo_sync_service.ts +++ b/app/kits/services/repo_sync_service.ts @@ -10,9 +10,7 @@ import { } from '@atproto/oauth-client-node' import { Client, isBlobRef, getBlobCidString } from '@atproto/lex' import gunplaKit from '#lexicons/build/gunpla/kit' -import gunplaBuildLog from '#lexicons/build/gunpla/buildLog' import Kit from '#kits/models/kit' -import BuildLog from '#kits/models/build_log' import { DateTime } from 'luxon' export interface SyncResult { @@ -20,7 +18,6 @@ export interface SyncResult { handle: string pdsEndpoint: string kitsSynced: number - logsSynced: number } export interface SyncAccountResult { @@ -33,7 +30,6 @@ export interface SyncAccountResult { export interface BatchSyncResult { results: SyncAccountResult[] totalKits: number - totalLogs: number hasFailures: boolean } @@ -96,29 +92,6 @@ export class RepoSyncService { return count } - protected async syncBuildLogs(client: Client, repoDid: AtprotoDid): Promise { - let count = 0 - for await (const record of client.listAll(gunplaBuildLog, { repo: repoDid })) { - if (!record.valid) continue - - const log = record.value - await BuildLog.updateOrCreate( - { uri: record.uri }, - { - cid: record.cid, - did: repoDid, - kitUri: log.kit, - stage: log.stage ?? null, - text: log.text ?? null, - recordCreatedAt: log.createdAt ? DateTime.fromISO(log.createdAt) : null, - indexedAt: DateTime.now(), - } - ) - count++ - } - return count - } - async syncUser(identifier: string): Promise { const identity = await this.identityService.resolve(identifier) if (!identity) { @@ -135,7 +108,6 @@ export class RepoSyncService { const repoDid = asAtprotoDid(did) const kitsSynced = await this.syncKits(client, repoDid) - const logsSynced = await this.syncBuildLogs(client, repoDid) await this.profileService.syncProfile(did, handle) @@ -144,14 +116,12 @@ export class RepoSyncService { handle, pdsEndpoint, kitsSynced, - logsSynced, } } async syncUsers(identifiers: string[]): Promise { const results: SyncAccountResult[] = [] let totalKits = 0 - let totalLogs = 0 let hasFailures = false for (const identifier of identifiers) { @@ -163,7 +133,6 @@ export class RepoSyncService { data: syncData, }) totalKits += syncData.kitsSynced - totalLogs += syncData.logsSynced } catch (error) { hasFailures = true results.push({ @@ -177,7 +146,6 @@ export class RepoSyncService { return { results, totalKits, - totalLogs, hasFailures, } } diff --git a/app/kits/transformers/build_log_transformer.ts b/app/kits/transformers/build_log_transformer.ts deleted file mode 100644 index dcc81b5..0000000 --- a/app/kits/transformers/build_log_transformer.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseTransformer } from '@adonisjs/core/transformers' -import type BuildLog from '#kits/models/build_log' - -export default class BuildLogTransformer extends BaseTransformer { - toObject() { - return { - id: this.resource.id, - uri: this.resource.uri, - did: this.resource.did, - kitUri: this.resource.kitUri, - stage: this.resource.stage, - text: this.resource.text, - recordCreatedAt: this.resource.recordCreatedAt?.toISO() ?? null, - indexedAt: this.resource.indexedAt.toISO()!, - } - } - - forExploreDetail() { - return { - formattedDate: this.resource.recordCreatedAt?.toFormat('MMM yyyy') ?? null, - stage: this.resource.stage, - text: this.resource.text, - } - } -} diff --git a/app/kits/transformers/kit_transformer.ts b/app/kits/transformers/kit_transformer.ts index eda7945..f336756 100644 --- a/app/kits/transformers/kit_transformer.ts +++ b/app/kits/transformers/kit_transformer.ts @@ -2,7 +2,6 @@ import { BaseTransformer } from '@adonisjs/core/transformers' import type Kit from '#kits/models/kit' import { getBlobUrl } from '#kits/helpers/atproto' import { AtUri } from '@atproto/syntax' -import BuildLogTransformer from '#kits/transformers/build_log_transformer' import ProfileTransformer from '#profile/transformers/profile_transformer' export interface PhotoItem { @@ -60,9 +59,6 @@ export default class KitTransformer extends BaseTransformer { forProfileShelf() { return { ...this.toObject(), - buildLogs: BuildLogTransformer.transform(this.resource.buildLogs ?? []).useVariant( - 'forExploreDetail' - ), } } @@ -96,9 +92,6 @@ export default class KitTransformer extends BaseTransformer { grade: this.resource.grade, scale: this.resource.scale, photos, - buildLogs: BuildLogTransformer.transform(this.resource.buildLogs ?? []).useVariant( - 'forExploreDetail' - ), } } } diff --git a/app/profile/controllers/show_profile_controller.ts b/app/profile/controllers/show_profile_controller.ts index 924d7f3..5c93dc3 100644 --- a/app/profile/controllers/show_profile_controller.ts +++ b/app/profile/controllers/show_profile_controller.ts @@ -23,7 +23,6 @@ export default class ShowProfileController { description: profile.description, displayName: profile.displayName, kits: KitTransformer.transform(profile.kits).useVariant('forProfileShelf'), - buildLogs: profile.buildLogs, stats: profile.stats, achievements: profile.achievements, }) diff --git a/app/profile/models/profile.ts b/app/profile/models/profile.ts index 3eb2edd..78bfb36 100644 --- a/app/profile/models/profile.ts +++ b/app/profile/models/profile.ts @@ -2,7 +2,6 @@ import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm' import type { HasMany } from '@adonisjs/lucid/types/relations' import { DateTime } from 'luxon' import Kit from '#kits/models/kit' -import BuildLog from '#kits/models/build_log' export default class Profile extends BaseModel { static table = 'profiles' @@ -35,7 +34,4 @@ export default class Profile extends BaseModel { @hasMany(() => Kit, { foreignKey: 'did', localKey: 'did' }) declare kits: HasMany - - @hasMany(() => BuildLog, { foreignKey: 'did', localKey: 'did' }) - declare buildLogs: HasMany } diff --git a/app/profile/services/achievements_service.ts b/app/profile/services/achievements_service.ts index 896edbe..a8fc26b 100644 --- a/app/profile/services/achievements_service.ts +++ b/app/profile/services/achievements_service.ts @@ -1,5 +1,4 @@ import type Kit from '#kits/models/kit' -import type BuildLog from '#kits/models/build_log' import type { JSONDataTypes } from '@adonisjs/core/types/transformers' export type Achievement = { @@ -17,12 +16,11 @@ export type ProfileStats = { completed: number inProgress: number backlog: number - buildLogs: number [key: string]: JSONDataTypes } export class AchievementsService { - computeStats(kits: Kit[], buildLogs: BuildLog[]): ProfileStats { + computeStats(kits: Kit[]): ProfileStats { const completed = kits.filter((k) => k.status === 'done').length const inProgress = kits.filter((k) => k.status === 'in-progress').length const backlog = kits.filter((k) => k.status === 'backlog').length @@ -32,11 +30,10 @@ export class AchievementsService { completed, inProgress, backlog, - buildLogs: buildLogs.length, } } - computeAchievements(kits: Kit[], buildLogs: BuildLog[]): Achievement[] { + computeAchievements(kits: Kit[]): Achievement[] { const completedKits = kits.filter((k) => k.status === 'done') const completedCount = completedKits.length const backlogCount = kits.filter((k) => k.status === 'backlog').length @@ -126,14 +123,6 @@ export class AchievementsService { unlocked: completedGrades.size >= 6, progress: { current: Math.min(completedGrades.size, 6), total: 6 }, }, - { - id: 'build-chronicler', - name: 'Build Chronicler', - description: 'Publish 3 or more build logs', - icon: '📝', - unlocked: buildLogs.length >= 3, - progress: { current: Math.min(buildLogs.length, 3), total: 3 }, - }, ] } } diff --git a/app/profile/services/profile_service.ts b/app/profile/services/profile_service.ts index d436e92..3235182 100644 --- a/app/profile/services/profile_service.ts +++ b/app/profile/services/profile_service.ts @@ -1,9 +1,7 @@ import { inject } from '@adonisjs/core' import { DateTime } from 'luxon' import Kit from '#kits/models/kit' -import BuildLog from '#kits/models/build_log' import Profile from '#profile/models/profile' -import BuildLogTransformer from '#kits/transformers/build_log_transformer' import { AchievementsService } from '#profile/services/achievements_service' import { IdentityService } from '#profile/services/identity_service' @@ -90,22 +88,14 @@ export class ProfileService { } } - const [kits, buildLogs] = await Promise.all([ - Kit.query() - .where('did', profile.did) - .preload('profile') - .preload('buildLogs') - .orderBy('indexed_at', 'desc') - .limit(100), - BuildLog.query() - .where('did', profile.did) - .preload('profile') - .orderBy('indexed_at', 'desc') - .limit(50), - ]) + const kits = await Kit.query() + .where('did', profile.did) + .preload('profile') + .orderBy('indexed_at', 'desc') + .limit(100) - const stats = this.achievementsService.computeStats(kits, buildLogs) - const achievements = this.achievementsService.computeAchievements(kits, buildLogs) + const stats = this.achievementsService.computeStats(kits) + const achievements = this.achievementsService.computeAchievements(kits) return { handle: profile.handle, @@ -114,7 +104,6 @@ export class ProfileService { description: profile.description, displayName: profile.displayName, kits, - buildLogs: BuildLogTransformer.transform(buildLogs), stats, achievements, } diff --git a/commands/jetstream/listen.ts b/commands/jetstream/listen.ts index 7e0378c..55ba1ba 100644 --- a/commands/jetstream/listen.ts +++ b/commands/jetstream/listen.ts @@ -16,7 +16,7 @@ export default class JetstreamListen extends BaseCommand { @flags.string({ description: 'Comma-separated list of collections to subscribe to', - default: 'build.gunpla.kit,build.gunpla.buildLog', + default: 'build.gunpla.kit', }) declare collections: string diff --git a/commands/kits/delete_all_logs.ts b/commands/kits/delete_all_logs.ts deleted file mode 100644 index 754b39b..0000000 --- a/commands/kits/delete_all_logs.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { BaseCommand, args } from '@adonisjs/core/ace' -import type { CommandOptions } from '@adonisjs/core/types/ace' -import { Client } from '@atproto/lex' -import { AtUri } from '@atproto/syntax' -import { main as buildLogSchema } from '#lexicons/build/gunpla/buildLog' - -export default class KitsDeleteAllLogs extends BaseCommand { - static commandName = 'kits:delete-all-logs' - static description = 'Delete all build.gunpla.buildLog records for a given DID' - - static options: CommandOptions = { startApp: true } - - @args.string({ description: 'DID of the user whose build logs to delete' }) - declare did: string - - async run() { - const config = await this.app.container.make('atproto.oauth.config') - const oauthService = await this.app.container.make('atproto.oauth.client') - await oauthService.configure(config.publicUrl, config.metadata, config.jwks) - const session = await oauthService.client.restore(this.did) - const client = new Client(session) - - let cursor: string | undefined - let deleted = 0 - - do { - const { records, cursor: next } = await client.list(buildLogSchema, { cursor }) - cursor = next - - for (const record of records) { - const atUri = new AtUri(record.uri) - const rkey = atUri.rkey - await client.delete(buildLogSchema, { rkey }) - this.logger.info(`deleted ${record.uri}`) - deleted++ - } - } while (cursor) - - this.logger.success(`deleted ${deleted} build log record(s)`) - } -} diff --git a/commands/kits/sync.ts b/commands/kits/sync.ts index 165bcd3..569964a 100644 --- a/commands/kits/sync.ts +++ b/commands/kits/sync.ts @@ -5,8 +5,7 @@ import { RepoSyncService } from '#kits/services/repo_sync_service' export default class KitsSync extends BaseCommand { static commandName = 'kits:sync' - static description = - 'Fetch and populate kit and build log records from a user PDS into the database' + static description = 'Fetch and populate kit records from a user PDS into the database' static options: CommandOptions = { startApp: true } @@ -30,7 +29,7 @@ export default class KitsSync extends BaseCommand { if (res.success && res.data) { const data = res.data this.logger.success( - `Synced @${data.handle} (${data.did}) from ${data.pdsEndpoint}: ${data.kitsSynced} kit(s), ${data.logsSynced} build log(s)` + `Synced @${data.handle} (${data.did}) from ${data.pdsEndpoint}: ${data.kitsSynced} kit(s)` ) if (data.kitsSynced === 0) { this.logger.info(`0 kit records found for @${data.handle}`) diff --git a/config/atproto_oauth.ts b/config/atproto_oauth.ts index 9c18eae..a2eb75e 100644 --- a/config/atproto_oauth.ts +++ b/config/atproto_oauth.ts @@ -19,9 +19,6 @@ export default defineConfig({ 'repo:build.gunpla.kit?action=create', 'repo:build.gunpla.kit?action=update', 'repo:build.gunpla.kit?action=delete', - 'repo:build.gunpla.buildLog?action=create', - 'repo:build.gunpla.buildLog?action=update', - 'repo:build.gunpla.buildLog?action=delete', ].join(' '), // logo_uri: 'https://my-app.com/logo.png', // tos_uri: 'https://my-app.com/tos', diff --git a/database/factories/build_log_factory.ts b/database/factories/build_log_factory.ts deleted file mode 100644 index b69bc43..0000000 --- a/database/factories/build_log_factory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Factory from '@adonisjs/lucid/factories' -import { DateTime } from 'luxon' -import BuildLog from '#kits/models/build_log' - -export const BuildLogFactory = Factory.define(BuildLog, ({ faker }) => { - const did = `did:plc:${faker.string.alphanumeric(24)}` - const rkey = faker.string.alphanumeric(13) - const kitRkey = faker.string.alphanumeric(13) - return { - uri: `at://${did}/build.gunpla.buildLog/${rkey}`, - cid: null, - did, - kitUri: `at://${did}/build.gunpla.kit/${kitRkey}`, - stage: null, - text: null, - recordCreatedAt: null, - indexedAt: DateTime.now(), - } -}).build() diff --git a/database/migrations/1791394489248_create_drop_build_logs_table.ts b/database/migrations/1791394489248_create_drop_build_logs_table.ts new file mode 100644 index 0000000..15409da --- /dev/null +++ b/database/migrations/1791394489248_create_drop_build_logs_table.ts @@ -0,0 +1,25 @@ +import { BaseSchema } from '@adonisjs/lucid/schema' + +export default class extends BaseSchema { + protected tableName = 'build_logs' + + async up() { + this.schema.dropTable(this.tableName) + } + + async down() { + this.schema.createTable(this.tableName, (table) => { + table.increments('id') + table.string('uri').notNullable().unique() + table.string('cid').nullable() + table.string('did').notNullable().index() + table.string('kit_uri').notNullable().index() + table.string('stage').nullable() + table.text('text').nullable() + table.timestamp('record_created_at').nullable() + table.timestamp('indexed_at').notNullable() + table.timestamp('created_at') + table.timestamp('updated_at') + }) + } +} diff --git a/database/schema.ts b/database/schema.ts index 7aa54e1..3ffc016 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -7,33 +7,6 @@ import { BaseModel, column } from '@adonisjs/lucid/orm' import { DateTime } from 'luxon' -export class BuildLogSchema extends BaseModel { - static $columns = ['cid', 'createdAt', 'did', 'id', 'indexedAt', 'kitUri', 'recordCreatedAt', 'stage', 'text', 'updatedAt', 'uri'] as const - $columns = BuildLogSchema.$columns - @column() - declare cid: string | null - @column.dateTime({ autoCreate: true }) - declare createdAt: DateTime | null - @column() - declare did: string - @column({ isPrimary: true }) - declare id: number - @column.dateTime() - declare indexedAt: DateTime - @column() - declare kitUri: string - @column.dateTime() - declare recordCreatedAt: DateTime | null - @column() - declare stage: string | null - @column() - declare text: string | null - @column.dateTime({ autoCreate: true, autoUpdate: true }) - declare updatedAt: DateTime | null - @column() - declare uri: string -} - export class KitSchema extends BaseModel { static $columns = ['acquiredAt', 'cid', 'createdAt', 'did', 'grade', 'id', 'indexedAt', 'manufacturer', 'name', 'photoCid', 'photos', 'recordCreatedAt', 'scale', 'status', 'updatedAt', 'uri'] as const $columns = KitSchema.$columns diff --git a/database/seeders/sync_user_records_seeder.ts b/database/seeders/sync_user_records_seeder.ts index beb17f1..58651f0 100644 --- a/database/seeders/sync_user_records_seeder.ts +++ b/database/seeders/sync_user_records_seeder.ts @@ -17,7 +17,7 @@ export default class extends BaseSeeder { const syncService = await app.container.make(RepoSyncService) const result = await syncService.syncUser(syncUser) logger.info( - `Synced @${result.handle} (${result.kitsSynced} kits, ${result.logsSynced} logs) from ${result.pdsEndpoint}` + `Synced @${result.handle} (${result.kitsSynced} kits) from ${result.pdsEndpoint}` ) } } diff --git a/inertia/components/shelf_kit_detail.tsx b/inertia/components/shelf_kit_detail.tsx index beef39b..9eec426 100644 --- a/inertia/components/shelf_kit_detail.tsx +++ b/inertia/components/shelf_kit_detail.tsx @@ -39,37 +39,6 @@ export function ShelfKitDetail({ kit, onClose }: { kit: KitShelf; onClose: () => - -
- {kit.buildLogs && kit.buildLogs.length > 0 ? ( -
-

- Build log -

-
    - {kit.buildLogs.map((entry, i) => ( -
  1. - {i < kit.buildLogs!.length - 1 && ( -
    - )} -
    -
    - - {entry.formattedDate} - -

    {entry.stage}

    -

    {entry.text}

    -
    -
  2. - ))} -
-
- ) : ( -

- No build logs yet. -

- )} -
) diff --git a/inertia/pages/explore/kit_detail.tsx b/inertia/pages/explore/kit_detail.tsx index 4458f18..b16ae37 100644 --- a/inertia/pages/explore/kit_detail.tsx +++ b/inertia/pages/explore/kit_detail.tsx @@ -51,40 +51,6 @@ export function KitDetail({ kit, onClose }: { kit: KitExplore; onClose: () => vo - - {/* Scrollable body */} -
- {kit.buildLogs && kit.buildLogs.length > 0 && ( -
-

- Build log -

-
    - {kit.buildLogs.map((entry, i) => ( -
  1. - {i < kit.buildLogs!.length - 1 && ( -
    - )} -
    -
    - - {entry.formattedDate} - -

    {entry.stage}

    -

    {entry.text}

    -
    -
  2. - ))} -
-
- )} - - {(!kit.buildLogs || kit.buildLogs.length === 0) && ( -

- No details yet. -

- )} -
) diff --git a/inertia/pages/profile.tsx b/inertia/pages/profile.tsx index 8c53271..04aede6 100644 --- a/inertia/pages/profile.tsx +++ b/inertia/pages/profile.tsx @@ -21,19 +21,14 @@ import { Plus, Package, Images, - Activity, - Hammer, - CheckCircle2, Check, } from 'lucide-react' import type { InertiaProps } from '~/types' -import type { InferData, InferVariants } from '@adonisjs/http-transformers/types' +import type { InferVariants } from '@adonisjs/http-transformers/types' import type KitTransformer from '#kits/transformers/kit_transformer' -import type BuildLogTransformer from '#kits/transformers/build_log_transformer' import type { Achievement, ProfileStats } from '#profile/services/achievements_service' type KitShelf = InferVariants['forProfileShelf'] -type BuildLogRow = InferData type PageProps = InertiaProps<{ handle: string @@ -42,18 +37,10 @@ type PageProps = InertiaProps<{ description?: string | null displayName?: string | null kits: KitShelf[] - buildLogs: BuildLogRow[] stats: ProfileStats achievements: Achievement[] }> -function buildLogIcon(stage?: string | null) { - const s = (stage ?? '').toLowerCase() - if (s.includes('done') || s.includes('complet') || s.includes('finish')) return CheckCircle2 - if (s.includes('start') || s.includes('begin') || s.includes('progress')) return Hammer - return Activity -} - export default function Profile({ handle, did, @@ -61,7 +48,6 @@ export default function Profile({ description, displayName, kits, - buildLogs, stats, achievements, }: PageProps) { @@ -123,7 +109,6 @@ export default function Profile({ { label: 'Done', value: stats.completed }, { label: 'Active', value: stats.inProgress }, { label: 'Backlog', value: stats.backlog }, - { label: 'Logs', value: stats.buildLogs }, ] : [] @@ -489,50 +474,6 @@ export default function Profile({ )} - -
-

- Build logs - - {buildLogs.length} - -

- {buildLogs.length === 0 ? ( -

- No build logs yet. -

- ) : ( -
    - {buildLogs.map((log, i) => { - const Icon = buildLogIcon(log.stage) - return ( -
  • - - - -
    -

    {log.stage ?? 'Build update'}

    - {log.text && ( -

    - {log.text} -

    - )} -

    - {timeAgo(log.indexedAt)} -

    -
    -
  • - ) - })} -
- )} -
diff --git a/services/jetstream_service.ts b/services/jetstream_service.ts index 6aefdf6..b660ff5 100644 --- a/services/jetstream_service.ts +++ b/services/jetstream_service.ts @@ -3,13 +3,11 @@ import logger from '@adonisjs/core/services/logger' import { inject } from '@adonisjs/core' import env from '#start/env' import Kit from '#kits/models/kit' -import BuildLog from '#kits/models/build_log' import Profile from '#profile/models/profile' import { DateTime } from 'luxon' import { AtUri } from '@atproto/syntax' import { lexParse, getBlobCidString, isBlobRef } from '@atproto/lex' import { main as gunplaKitLexicon } from '#lexicons/build/gunpla/kit' -import { main as gunplaBuildLogLexicon } from '#lexicons/build/gunpla/buildLog' import fs from 'node:fs' import WebSocket from 'ws' @@ -201,39 +199,6 @@ export class JetstreamService { ) await this.broadcast('kits', { type: operation, uri }) } - - if (collection === 'build.gunpla.buildLog') { - if (operation === 'delete') { - logger.info(`[Jetstream] deleting build log ${uri}`) - await BuildLog.query().where('uri', uri).delete() - await this.broadcast('kits', { type: 'delete_log', uri }) - return - } - logger.info(`[Jetstream] ${operation} build log ${uri}`) - const { record, cid } = event.commit - const buildLogValidation = gunplaBuildLogLexicon.safeValidate(record) - if (!buildLogValidation.success) { - logger.warn( - { collection: 'build.gunpla.buildLog', rkey, error: buildLogValidation.error }, - '[Jetstream] invalid build log record — skipping' - ) - return - } - await this.resolveHandle(did) - await BuildLog.updateOrCreate( - { uri }, - { - cid, - did, - kitUri: record.kit as string, - stage: record.stage as string, - text: record.text as string | undefined, - recordCreatedAt: record.createdAt ? DateTime.fromISO(record.createdAt as string) : null, - indexedAt: DateTime.now(), - } - ) - await this.broadcast('kits', { type: operation, uri }) - } } start(): void { @@ -319,5 +284,5 @@ export class JetstreamService { } export default new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], + wantedCollections: ['build.gunpla.kit'], }) diff --git a/tests/functional/explore/render.spec.ts b/tests/functional/explore/render.spec.ts index c3fd9d8..0abac1e 100644 --- a/tests/functional/explore/render.spec.ts +++ b/tests/functional/explore/render.spec.ts @@ -1,14 +1,12 @@ import { test } from '@japa/runner' import testUtils from '@adonisjs/core/services/test_utils' -import { DateTime } from 'luxon' import { KitFactory } from '#database/factories/kit_factory' import { ProfileFactory } from '#database/factories/profile_factory' -import { BuildLogFactory } from '#database/factories/build_log_factory' test.group('ExploreController.render', (group) => { group.each.setup(() => testUtils.db().truncate()) - test('explore renders completed kits with transformer-shaped fields and build logs', async ({ + test('explore renders completed kits with transformer-shaped fields', async ({ client, }) => { await ProfileFactory.merge({ @@ -16,7 +14,7 @@ test.group('ExploreController.render', (group) => { handle: 'amuro.bsky.social', }).create() - const kit = await KitFactory.merge({ + await KitFactory.merge({ did: 'did:plc:amuro', status: 'done', name: 'HG Aerial', @@ -24,14 +22,6 @@ test.group('ExploreController.render', (group) => { photoCid: 'bafyfakecid', }).create() - await BuildLogFactory.merge({ - did: 'did:plc:amuro', - kitUri: kit.uri, - stage: 'Painting', - text: 'Top coat done', - recordCreatedAt: DateTime.fromISO('2024-03-15'), - }).create() - const response = await client.visit('explore').withInertia() response.assertStatus(200) @@ -43,7 +33,6 @@ test.group('ExploreController.render', (group) => { name: 'HG Aerial', handle: 'amuro.bsky.social', grade: 'HG', - buildLogs: [{ stage: 'Painting', text: 'Top coat done', formattedDate: 'Mar 2024' }], }, ], metadata: { currentPage: 1 }, diff --git a/tests/functional/profile.spec.ts b/tests/functional/profile.spec.ts index 3340af3..688959f 100644 --- a/tests/functional/profile.spec.ts +++ b/tests/functional/profile.spec.ts @@ -1,7 +1,6 @@ import { test } from '@japa/runner' import testUtils from '@adonisjs/core/services/test_utils' import Kit from '#kits/models/kit' -import BuildLog from '#kits/models/build_log' import { DateTime } from 'luxon' import { IdentityService } from '#profile/services/identity_service' import { ProfileFactory } from '#database/factories/profile_factory' @@ -43,62 +42,15 @@ test.group('Profile', (group) => { response.assertInertiaPropsContains({ handle: 'empty.user', kits: [], - buildLogs: [], stats: { totalKits: 0, completed: 0, inProgress: 0, backlog: 0, - buildLogs: 0, }, }) }) - test('renders profile with transformer-shaped kits and build logs per kit', async ({ - client, - swap, - assert, - }) => { - swap(IdentityService, new FakeIdentityService()) - - const did = 'did:plc:amurobskysocial' - const handle = 'amuro.bsky.social' - - await Kit.create({ - uri: `at://${did}/build.gunpla.kit/kit1`, - did, - name: 'HG Aerial', - status: 'done', - photoCid: 'bafyfakecid', - indexedAt: DateTime.now(), - }) - - await BuildLog.create({ - uri: `at://${did}/build.gunpla.buildLog/log1`, - did, - kitUri: `at://${did}/build.gunpla.kit/kit1`, - stage: 'Painting', - recordCreatedAt: DateTime.fromISO('2024-03-15'), - indexedAt: DateTime.now(), - }) - - const response = await client.visit('profile', { handle }).withInertia() - - response.assertInertiaPropsContains({ - kits: [ - { - name: 'HG Aerial', - status: 'done', - photoCount: 1, - buildLogs: [{ stage: 'Painting', formattedDate: 'Mar 2024' }], - }, - ], - buildLogs: [{ stage: 'Painting' }], - }) - - assert.typeOf(response.inertiaProps.buildLogs[0].recordCreatedAt, 'string') - }) - test('renders profile with unlocked Infinity Gauntlet achievement when user completed 6 grades', async ({ client, db, diff --git a/tests/helpers/fake_repo_sync_service.ts b/tests/helpers/fake_repo_sync_service.ts index 2a907b7..d5c7fc2 100644 --- a/tests/helpers/fake_repo_sync_service.ts +++ b/tests/helpers/fake_repo_sync_service.ts @@ -26,7 +26,6 @@ export class FakeRepoSyncService extends RepoSyncService { handle: identifier.includes('.') ? identifier : `${identifier}.bsky.social`, pdsEndpoint: 'https://fake.pds', kitsSynced: 0, - logsSynced: 0, } } diff --git a/tests/helpers/testable_repo_sync_service.ts b/tests/helpers/testable_repo_sync_service.ts index a63feda..4313c74 100644 --- a/tests/helpers/testable_repo_sync_service.ts +++ b/tests/helpers/testable_repo_sync_service.ts @@ -7,7 +7,6 @@ import type { AtprotoDid } from '@atproto/oauth-client-node' export interface TestableRepoSyncOptions { pdsEndpoint?: string | null kitsSynced?: number - logsSynced?: number } export class TestableRepoSyncService extends RepoSyncService { @@ -26,8 +25,4 @@ export class TestableRepoSyncService extends RepoSyncService { protected override async syncKits(_client: Client, _repoDid: AtprotoDid): Promise { return this.options.kitsSynced ?? 2 } - - protected override async syncBuildLogs(_client: Client, _repoDid: AtprotoDid): Promise { - return this.options.logsSynced ?? 1 - } } diff --git a/tests/unit/achievements_service.spec.ts b/tests/unit/achievements_service.spec.ts index e3c04af..73b188c 100644 --- a/tests/unit/achievements_service.spec.ts +++ b/tests/unit/achievements_service.spec.ts @@ -2,7 +2,6 @@ import { test } from '@japa/runner' import type { ModelAttributes } from '@adonisjs/lucid/types/model' import { AchievementsService } from '#profile/services/achievements_service' import Kit from '#kits/models/kit' -import BuildLog from '#kits/models/build_log' function createKit(overrides: Partial> = {}): Kit { const kit = new Kit() @@ -19,24 +18,10 @@ function createKit(overrides: Partial> = {}): Kit { return kit } -function createLog(overrides: Partial> = {}): BuildLog { - const log = new BuildLog() - log.merge({ - id: 1, - uri: 'at://did:plc:test/build.gunpla.buildLog/1', - did: 'did:plc:test', - kitUri: 'at://did:plc:test/build.gunpla.kit/1', - stage: 'snap-fit', - text: 'Building log', - ...overrides, - }) - return log -} - test.group('AchievementsService | computeStats', () => { const service = new AchievementsService() - test('computes counts across various kit statuses and build logs', ({ assert }) => { + test('computes counts across various kit statuses', ({ assert }) => { const kits = [ createKit({ status: 'done' }), createKit({ status: 'done' }), @@ -45,25 +30,22 @@ test.group('AchievementsService | computeStats', () => { createKit({ status: 'backlog' }), createKit({ status: 'backlog' }), ] - const logs = [createLog(), createLog()] - const stats = service.computeStats(kits, logs) + const stats = service.computeStats(kits) assert.equal(stats.totalKits, 6) assert.equal(stats.completed, 2) assert.equal(stats.inProgress, 1) assert.equal(stats.backlog, 3) - assert.equal(stats.buildLogs, 2) }) test('handles empty collections safely', ({ assert }) => { - const stats = service.computeStats([], []) + const stats = service.computeStats([]) assert.equal(stats.totalKits, 0) assert.equal(stats.completed, 0) assert.equal(stats.inProgress, 0) assert.equal(stats.backlog, 0) - assert.equal(stats.buildLogs, 0) }) }) @@ -71,7 +53,7 @@ test.group('AchievementsService | computeAchievements', () => { const service = new AchievementsService() test('unlocks first-step and hangar-master based on completed counts', ({ assert }) => { - const zeroKits = service.computeAchievements([], []) + const zeroKits = service.computeAchievements([]) const firstStepZero = zeroKits.find((a) => a.id === 'first-step') const hangarMasterZero = zeroKits.find((a) => a.id === 'hangar-master') @@ -87,7 +69,7 @@ test.group('AchievementsService | computeAchievements', () => { createKit({ status: 'done' }), createKit({ status: 'done' }), ] - const fiveAch = service.computeAchievements(fiveKits, []) + const fiveAch = service.computeAchievements(fiveKits) const firstStepFive = fiveAch.find((a) => a.id === 'first-step') const hangarMasterFive = fiveAch.find((a) => a.id === 'hangar-master') @@ -104,13 +86,13 @@ test.group('AchievementsService | computeAchievements', () => { createKit({ status: 'backlog' }), createKit({ status: 'backlog' }), ] - const achFour = service.computeAchievements(fourBacklog, []) + const achFour = service.computeAchievements(fourBacklog) const bhFour = achFour.find((a) => a.id === 'backlog-hoarder') assert.isFalse(bhFour?.unlocked) assert.deepEqual(bhFour?.progress, { current: 4, total: 5 }) const fiveBacklog = [...fourBacklog, createKit({ status: 'backlog' })] - const achFive = service.computeAchievements(fiveBacklog, []) + const achFive = service.computeAchievements(fiveBacklog) const bhFive = achFive.find((a) => a.id === 'backlog-hoarder') assert.isTrue(bhFive?.unlocked) assert.deepEqual(bhFive?.progress, { current: 5, total: 5 }) @@ -120,16 +102,16 @@ test.group('AchievementsService | computeAchievements', () => { assert, }) => { const mgKitOnly = [createKit({ grade: 'MG 1/100', status: 'done' })] - const ach1 = service.computeAchievements(mgKitOnly, []) + const ach1 = service.computeAchievements(mgKitOnly) assert.isTrue(ach1.find((a) => a.id === 'master-grade-club')?.unlocked) assert.isFalse(ach1.find((a) => a.id === 'peak-engineering')?.unlocked) const mgInBacklog = [createKit({ grade: 'MG', status: 'backlog' })] - const ach2 = service.computeAchievements(mgInBacklog, []) + const ach2 = service.computeAchievements(mgInBacklog) assert.isFalse(ach2.find((a) => a.id === 'master-grade-club')?.unlocked) const pgKit = [createKit({ grade: 'Perfect Grade', status: 'done' })] - const ach3 = service.computeAchievements(pgKit, []) + const ach3 = service.computeAchievements(pgKit) assert.isTrue(ach3.find((a) => a.id === 'peak-engineering')?.unlocked) }) @@ -145,7 +127,7 @@ test.group('AchievementsService | computeAchievements', () => { createKit({ grade: 'SD BB Senshi', status: 'backlog' }), // Ignored because in backlog ] - const achFive = service.computeAchievements(fiveGrades, []) + const achFive = service.computeAchievements(fiveGrades) const gauntletFive = achFive.find((a) => a.id === 'infinity-gauntlet') assert.isDefined(gauntletFive) @@ -159,18 +141,10 @@ test.group('AchievementsService | computeAchievements', () => { createKit({ grade: 'SD BB Senshi', status: 'done' }), ] - const achSix = service.computeAchievements(sixGrades, []) + const achSix = service.computeAchievements(sixGrades) const gauntletSix = achSix.find((a) => a.id === 'infinity-gauntlet') assert.isTrue(gauntletSix?.unlocked) assert.deepEqual(gauntletSix?.progress, { current: 6, total: 6 }) }) - - test('unlocks build-chronicler with 3 or more build logs', ({ assert }) => { - const twoLogs = service.computeAchievements([], [createLog(), createLog()]) - assert.isFalse(twoLogs.find((a) => a.id === 'build-chronicler')?.unlocked) - - const threeLogs = service.computeAchievements([], [createLog(), createLog(), createLog()]) - assert.isTrue(threeLogs.find((a) => a.id === 'build-chronicler')?.unlocked) - }) }) diff --git a/tests/unit/commands/kits_sync.spec.ts b/tests/unit/commands/kits_sync.spec.ts index d2903ba..b82901a 100644 --- a/tests/unit/commands/kits_sync.spec.ts +++ b/tests/unit/commands/kits_sync.spec.ts @@ -31,7 +31,6 @@ test.group('Command kits:sync', (group) => { handle: 'grld.dev', pdsEndpoint: 'https://eurosky.social', kitsSynced: 3, - logsSynced: 1, }), }) ) @@ -45,7 +44,6 @@ test.group('Command kits:sync', (group) => { command.assertLogMatches(/did:plc:abiaoe3hpp2cqiuptrss75fp/) command.assertLogMatches(/https:\/\/eurosky\.social/) command.assertLogMatches(/3 kit\(s\)/) - command.assertLogMatches(/1 build log\(s\)/) command.assertLogMatches(/Profile: \/@grld\.dev/) }) @@ -59,7 +57,6 @@ test.group('Command kits:sync', (group) => { handle: 'empty.bsky.social', pdsEndpoint: 'https://empty.pds', kitsSynced: 0, - logsSynced: 0, }), }) ) @@ -88,7 +85,6 @@ test.group('Command kits:sync', (group) => { handle: 'good.bsky.social', pdsEndpoint: 'https://good.pds', kitsSynced: 1, - logsSynced: 0, } }, }) diff --git a/tests/unit/jetstream_service.spec.ts b/tests/unit/jetstream_service.spec.ts index b4daa57..3a6c93f 100644 --- a/tests/unit/jetstream_service.spec.ts +++ b/tests/unit/jetstream_service.spec.ts @@ -2,15 +2,12 @@ import { test } from '@japa/runner' import testUtils from '@adonisjs/core/services/test_utils' import { JetstreamService } from '../../services/jetstream_service.js' import { KitFactory } from '../../database/factories/kit_factory.js' -import { BuildLogFactory } from '../../database/factories/build_log_factory.js' test.group('JetstreamService.processEvent', (group) => { group.each.setup(() => testUtils.db().truncate()) test('valid kit record creates a Kit row in the database', async ({ db }) => { - const service = new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], - }) + const service = new JetstreamService() await service.processEvent({ kind: 'commit', @@ -36,9 +33,7 @@ test.group('JetstreamService.processEvent', (group) => { }) test('invalid kit record is skipped without writing to the database', async ({ db }) => { - const service = new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], - }) + const service = new JetstreamService() await service.processEvent({ kind: 'commit', @@ -58,65 +53,13 @@ test.group('JetstreamService.processEvent', (group) => { }) }) - test('valid build log record creates a BuildLog row in the database', async ({ db }) => { - const service = new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], - }) - - await service.processEvent({ - kind: 'commit', - time_us: Date.now(), - did: 'did:plc:testfakeuser12345', - commit: { - operation: 'create', - collection: 'build.gunpla.buildLog', - rkey: 'logkey123', - cid: 'bafytest', - record: { - $type: 'build.gunpla.buildLog', - kit: 'at://did:plc:testfakeuser12345/build.gunpla.kit/testkey123', - createdAt: '2024-01-01T00:00:00.000Z', - }, - }, - }) - - await db.assertHas('build_logs', { - uri: 'at://did:plc:testfakeuser12345/build.gunpla.buildLog/logkey123', - }) - }) - - test('invalid build log record is skipped without writing to the database', async ({ db }) => { - const service = new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], - }) - - await service.processEvent({ - kind: 'commit', - time_us: Date.now(), - did: 'did:plc:testfakeuser12345', - commit: { - operation: 'create', - collection: 'build.gunpla.buildLog', - rkey: 'logkey123', - cid: 'bafytest', - record: { $type: 'build.gunpla.buildLog', text: 'hello' }, - }, - }) - - await db.assertMissing('build_logs', { - uri: 'at://did:plc:testfakeuser12345/build.gunpla.buildLog/logkey123', - }) - }) - test('kit delete event removes the Kit row from the database', async ({ db }) => { await KitFactory.merge({ uri: 'at://did:plc:testfakeuser12345/build.gunpla.kit/testkey123', did: 'did:plc:testfakeuser12345', }).create() - const service = new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], - }) + const service = new JetstreamService() await service.processEvent({ kind: 'commit', @@ -133,30 +76,4 @@ test.group('JetstreamService.processEvent', (group) => { uri: 'at://did:plc:testfakeuser12345/build.gunpla.kit/testkey123', }) }) - - test('build log delete event removes the BuildLog row from the database', async ({ db }) => { - await BuildLogFactory.merge({ - uri: 'at://did:plc:testfakeuser12345/build.gunpla.buildLog/logkey123', - did: 'did:plc:testfakeuser12345', - }).create() - - const service = new JetstreamService({ - wantedCollections: ['build.gunpla.kit', 'build.gunpla.buildLog'], - }) - - await service.processEvent({ - kind: 'commit', - time_us: Date.now(), - did: 'did:plc:testfakeuser12345', - commit: { - operation: 'delete', - collection: 'build.gunpla.buildLog', - rkey: 'logkey123', - }, - }) - - await db.assertMissing('build_logs', { - uri: 'at://did:plc:testfakeuser12345/build.gunpla.buildLog/logkey123', - }) - }) }) diff --git a/tests/unit/repo_sync_service.spec.ts b/tests/unit/repo_sync_service.spec.ts index 6132e31..f9119e8 100644 --- a/tests/unit/repo_sync_service.spec.ts +++ b/tests/unit/repo_sync_service.spec.ts @@ -37,7 +37,6 @@ test.group('RepoSyncService', () => { handle: 'user1.bsky.social', pdsEndpoint: 'https://pds1', kitsSynced: 2, - logsSynced: 1, } } return { @@ -45,13 +44,11 @@ test.group('RepoSyncService', () => { handle: 'user2.bsky.social', pdsEndpoint: 'https://pds2', kitsSynced: 3, - logsSynced: 0, } } const result = await customService.syncUsers(['user1.bsky.social', 'user2.bsky.social']) assert.equal(result.totalKits, 5) - assert.equal(result.totalLogs, 1) assert.isFalse(result.hasFailures) assert.lengthOf(result.results, 2) assert.isTrue(result.results[0].success) @@ -69,7 +66,6 @@ test.group('RepoSyncService', () => { handle: 'valid.bsky.social', pdsEndpoint: 'https://pds', kitsSynced: 4, - logsSynced: 2, } } @@ -80,7 +76,6 @@ test.group('RepoSyncService', () => { assert.equal(result.results[0].error, 'Connection refused to PDS') assert.isTrue(result.results[1].success) assert.equal(result.totalKits, 4) - assert.equal(result.totalLogs, 2) }) test('syncUser invokes ProfileService.syncProfile when profileService is injected', async ({ @@ -92,7 +87,6 @@ test.group('RepoSyncService', () => { const fakeProfile = new FakeProfileService() const service = new TestableRepoSyncService(fakeIdentity, fakeProfile, { kitsSynced: 2, - logsSynced: 1, }) const result = await service.syncUser('mockuser.bsky.social')