diff --git a/.adonisjs/client/registry/index.ts b/.adonisjs/client/registry/index.ts index c8d8342..ba764aa 100644 --- a/.adonisjs/client/registry/index.ts +++ b/.adonisjs/client/registry/index.ts @@ -48,12 +48,6 @@ const routes = { tokens: [{"old":"/login","type":0,"val":"login","end":""}], types: placeholder as Registry['login']['types'], }, - 'oauth.logout': { - methods: ["POST"], - pattern: '/oauth/logout', - tokens: [{"old":"/oauth/logout","type":0,"val":"oauth","end":""},{"old":"/oauth/logout","type":0,"val":"logout","end":""}], - types: placeholder as Registry['oauth.logout']['types'], - }, 'oauth.login': { methods: ["POST"], pattern: '/oauth/login', @@ -72,6 +66,12 @@ const routes = { tokens: [{"old":"/oauth/callback","type":0,"val":"oauth","end":""},{"old":"/oauth/callback","type":0,"val":"callback","end":""}], types: placeholder as Registry['oauth.callback']['types'], }, + 'oauth.logout': { + methods: ["POST"], + pattern: '/oauth/logout', + tokens: [{"old":"/oauth/logout","type":0,"val":"oauth","end":""},{"old":"/oauth/logout","type":0,"val":"logout","end":""}], + types: placeholder as Registry['oauth.logout']['types'], + }, 'shelf': { methods: ["GET","HEAD"], pattern: '/shelf', diff --git a/.adonisjs/client/registry/schema.d.ts b/.adonisjs/client/registry/schema.d.ts index 3713427..a54a8a2 100644 --- a/.adonisjs/client/registry/schema.d.ts +++ b/.adonisjs/client/registry/schema.d.ts @@ -51,8 +51,8 @@ export interface Registry { paramsTuple: [] params: {} query: {} - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> } } 'stats': { @@ -63,8 +63,8 @@ export interface Registry { paramsTuple: [] params: {} query: {} - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> } } 'explore': { @@ -75,8 +75,8 @@ export interface Registry { paramsTuple: [] params: {} query: ExtractQueryForGet> - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } } } 'login': { @@ -87,20 +87,8 @@ export interface Registry { paramsTuple: [] params: {} query: {} - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> - } - } - 'oauth.logout': { - methods: ["POST"] - pattern: '/oauth/logout' - types: { - body: {} - paramsTuple: [] - params: {} - query: {} - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> } } 'oauth.login': { @@ -111,8 +99,8 @@ export interface Registry { paramsTuple: [] params: {} query: ExtractQuery> - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } } } 'oauth.signup': { @@ -123,8 +111,8 @@ export interface Registry { paramsTuple: [] params: {} query: ExtractQuery> - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } } } 'oauth.callback': { @@ -135,8 +123,20 @@ export interface Registry { paramsTuple: [] params: {} query: {} - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> + } + } + 'oauth.logout': { + methods: ["POST"] + pattern: '/oauth/logout' + types: { + body: {} + paramsTuple: [] + params: {} + query: {} + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> } } 'shelf': { @@ -219,8 +219,8 @@ export interface Registry { paramsTuple: [ParamValue] params: { handle: ParamValue } query: ExtractQueryForGet> - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } } } 'kit': { @@ -231,8 +231,8 @@ export interface Registry { paramsTuple: [ParamValue, ParamValue] params: { handle: ParamValue; rkey: ParamValue } query: ExtractQueryForGet> - response: ExtractResponse>> - errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } + response: ExtractResponse>> + errorResponse: ExtractErrorResponse>> | { status: 422; response: { errors: SimpleError[] } } } } 'kits.comments.index': { diff --git a/.adonisjs/client/registry/tree.d.ts b/.adonisjs/client/registry/tree.d.ts index 9d32d00..ead4de6 100644 --- a/.adonisjs/client/registry/tree.d.ts +++ b/.adonisjs/client/registry/tree.d.ts @@ -10,10 +10,10 @@ export interface ApiDefinition { explore: typeof routes['explore'] login: typeof routes['login'] oauth: { - logout: typeof routes['oauth.logout'] login: typeof routes['oauth.login'] signup: typeof routes['oauth.signup'] callback: typeof routes['oauth.callback'] + logout: typeof routes['oauth.logout'] } shelf: typeof routes['shelf'] kits: { diff --git a/.adonisjs/server/controllers.ts b/.adonisjs/server/controllers.ts index 29642d4..d22239c 100644 --- a/.adonisjs/server/controllers.ts +++ b/.adonisjs/server/controllers.ts @@ -5,10 +5,8 @@ export const controllers = { auth: { - Callback: () => import('#app/auth/controllers/callback_controller'), - Login: () => import('#app/auth/controllers/login_controller'), - Logout: () => import('#app/auth/controllers/logout_controller'), - Signup: () => import('#app/auth/controllers/signup_controller'), + Registrations: () => import('#app/auth/controllers/registrations_controller'), + Sessions: () => import('#app/auth/controllers/sessions_controller'), }, explore: { Explore: () => import('#app/explore/controllers/explore_controller'), @@ -22,8 +20,7 @@ export const controllers = { Kits: () => import('#app/kits/controllers/kits_controller'), }, profile: { - ShowKit: () => import('#app/profile/controllers/show_kit_controller'), - ShowProfile: () => import('#app/profile/controllers/show_profile_controller'), + Profiles: () => import('#app/profile/controllers/profiles_controller'), }, stats: { Stats: () => import('#app/stats/controllers/stats_controller'), diff --git a/.adonisjs/server/routes.d.ts b/.adonisjs/server/routes.d.ts index 852d5c3..cd4c229 100644 --- a/.adonisjs/server/routes.d.ts +++ b/.adonisjs/server/routes.d.ts @@ -11,10 +11,10 @@ export type ScannedRoutes = { 'stats': { paramsTuple?: []; params?: {} } 'explore': { paramsTuple?: []; params?: {} } 'login': { paramsTuple?: []; params?: {} } - 'oauth.logout': { paramsTuple?: []; params?: {} } 'oauth.login': { paramsTuple?: []; params?: {} } 'oauth.signup': { paramsTuple?: []; params?: {} } 'oauth.callback': { paramsTuple?: []; params?: {} } + 'oauth.logout': { paramsTuple?: []; params?: {} } 'shelf': { paramsTuple?: []; params?: {} } 'kits.create': { paramsTuple?: []; params?: {} } 'kits.store': { paramsTuple?: []; params?: {} } @@ -60,9 +60,9 @@ export type ScannedRoutes = { POST: { 'subscribe': { paramsTuple?: []; params?: {} } 'unsubscribe': { paramsTuple?: []; params?: {} } - 'oauth.logout': { paramsTuple?: []; params?: {} } 'oauth.login': { paramsTuple?: []; params?: {} } 'oauth.signup': { paramsTuple?: []; params?: {} } + 'oauth.logout': { paramsTuple?: []; params?: {} } 'kits.store': { paramsTuple?: []; params?: {} } 'kits.comments.store': { paramsTuple: [ParamValue,ParamValue]; params: {'handle': ParamValue,'rkey': ParamValue} } 'kits.like': { paramsTuple: [ParamValue,ParamValue]; params: {'handle': ParamValue,'rkey': ParamValue} } diff --git a/app/kits/models/kit.ts b/app/kits/models/kit.ts index 18677aa..3845877 100644 --- a/app/kits/models/kit.ts +++ b/app/kits/models/kit.ts @@ -35,9 +35,6 @@ export default class Kit extends BaseModel { @column() declare status: string - @column() - declare photoCid: string | null - @column({ prepare: (value: string[] | null) => (value ? JSON.stringify(value) : null), consume: (value: string | string[] | null) => { @@ -83,6 +80,6 @@ export default class Kit extends BaseModel { } static async randomFeatured(): Promise { - return Kit.query().whereNotNull('photoCid').orderByRaw('RAND()').first() + return Kit.query().whereNotNull('photos').orderByRaw('RAND()').first() } } diff --git a/app/kits/services/kits_service.ts b/app/kits/services/kits_service.ts index 2814473..7e95389 100644 --- a/app/kits/services/kits_service.ts +++ b/app/kits/services/kits_service.ts @@ -203,7 +203,6 @@ export default class KitsService { data: CreateKitPayload, photoCids: string[] ): Promise { - const photoCid = photoCids[0] ?? null const acquiredAt = this.normalizeAcquiredDate(data.acquiredAt) await Kit.updateOrCreate( @@ -216,7 +215,6 @@ export default class KitsService { manufacturer: data.manufacturer, status: data.status, acquiredAt: acquiredAt ? DateTime.fromISO(acquiredAt) : null, - photoCid, photos: photoCids.length > 0 ? photoCids : null, recordCreatedAt: DateTime.now(), indexedAt: DateTime.now(), @@ -229,7 +227,6 @@ export default class KitsService { data: UpdateKitPayload, photoCids: string[] ): Promise { - const photoCid = photoCids[0] ?? null const acquiredAt = data.acquiredAt !== undefined ? this.normalizeAcquiredDate(data.acquiredAt) : undefined @@ -242,7 +239,6 @@ export default class KitsService { scale: data.scale ?? null, manufacturer: data.manufacturer ?? null, acquiredAt: acquiredAt ? DateTime.fromISO(acquiredAt) : null, - photoCid, photos: photoCids.length > 0 ? JSON.stringify(photoCids) : null, }) } diff --git a/app/kits/services/repo_sync_service.ts b/app/kits/services/repo_sync_service.ts index 2d9aec8..6266e99 100644 --- a/app/kits/services/repo_sync_service.ts +++ b/app/kits/services/repo_sync_service.ts @@ -180,7 +180,6 @@ export default class RepoSyncService { photoCids: string[] ) { const kit = record.value - const photoCid = photoCids[0] ?? null await Kit.updateOrCreate( { uri: record.uri }, @@ -193,7 +192,6 @@ export default class RepoSyncService { manufacturer: kit.manufacturer ?? null, status: kit.status ?? 'backlog', acquiredAt: kit.acquiredAt ? DateTime.fromISO(kit.acquiredAt) : null, - photoCid, photos: photoCids.length > 0 ? photoCids : null, recordCreatedAt: kit.createdAt ? DateTime.fromISO(kit.createdAt) : null, indexedAt: DateTime.now(), diff --git a/app/kits/transformers/kit_transformer.ts b/app/kits/transformers/kit_transformer.ts index afedcfa..8248971 100644 --- a/app/kits/transformers/kit_transformer.ts +++ b/app/kits/transformers/kit_transformer.ts @@ -26,8 +26,6 @@ export default class KitTransformer extends BaseTransformer { const photoCidList: string[] = [] if (Array.isArray(this.resource.photos) && this.resource.photos.length > 0) { photoCidList.push(...this.resource.photos) - } else if (this.resource.photoCid) { - photoCidList.push(this.resource.photoCid) } const coverCid = photoCidList[0] ?? null diff --git a/app/stats/services/stats_service.ts b/app/stats/services/stats_service.ts index 502ea57..ed6dd5c 100644 --- a/app/stats/services/stats_service.ts +++ b/app/stats/services/stats_service.ts @@ -18,11 +18,7 @@ export default class StatsService { db .from('kits') - .select( - db.raw( - 'COALESCE(SUM(COALESCE(JSON_LENGTH(photos), CASE WHEN photo_cid IS NOT NULL THEN 1 ELSE 0 END)), 0) as total' - ) - ) + .select(db.raw('COALESCE(SUM(COALESCE(JSON_LENGTH(photos), 0)), 0) as total')) .first(), db.from('kit_likes').count('* as total').first(), diff --git a/database/factories/kit_factory.ts b/database/factories/kit_factory.ts index 4292b36..743e98a 100644 --- a/database/factories/kit_factory.ts +++ b/database/factories/kit_factory.ts @@ -15,7 +15,6 @@ export const KitFactory = Factory.define(Kit, ({ faker }) => { scale: null, manufacturer: null, status: 'backlog', - photoCid: null, photos: null, acquiredAt: null, recordCreatedAt: null, diff --git a/database/migrations/1792010446348_alter_kits_table.ts b/database/migrations/1792010446348_alter_kits_table.ts new file mode 100644 index 0000000..9ec9028 --- /dev/null +++ b/database/migrations/1792010446348_alter_kits_table.ts @@ -0,0 +1,35 @@ +import { BaseSchema } from '@adonisjs/lucid/schema' + +export default class extends BaseSchema { + protected tableName = 'kits' + + async up() { + const hasColumn = await this.schema.hasColumn(this.tableName, 'photo_cid') + if (!hasColumn) return + + this.defer(async (db) => { + const rows = await db + .from(this.tableName) + .whereNull('photos') + .whereNotNull('photo_cid') + .select('id', 'photo_cid') + + for (const row of rows) { + await db + .from(this.tableName) + .where('id', row.id) + .update({ photos: JSON.stringify([row.photo_cid]) }) + } + }) + + this.schema.alterTable(this.tableName, (table) => { + table.dropColumn('photo_cid') + }) + } + + async down() { + this.schema.alterTable(this.tableName, (table) => { + table.string('photo_cid').nullable() + }) + } +} diff --git a/database/schema.ts b/database/schema.ts index fdedc3f..c6d117b 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -51,7 +51,7 @@ export class KitLikeSchema extends BaseModel { } 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 + static $columns = ['acquiredAt', 'cid', 'createdAt', 'did', 'grade', 'id', 'indexedAt', 'manufacturer', 'name', 'photos', 'recordCreatedAt', 'scale', 'status', 'updatedAt', 'uri'] as const $columns = KitSchema.$columns @column.dateTime() declare acquiredAt: DateTime | null @@ -72,8 +72,6 @@ export class KitSchema extends BaseModel { @column() declare name: string @column() - declare photoCid: string | null - @column() declare photos: any | null @column.dateTime() declare recordCreatedAt: DateTime | null diff --git a/services/jetstream_service.ts b/services/jetstream_service.ts index b04d974..fb90cf9 100644 --- a/services/jetstream_service.ts +++ b/services/jetstream_service.ts @@ -194,7 +194,6 @@ export class JetstreamService { manufacturer: record.manufacturer as string | undefined, acquiredAt: record.acquiredAt ? DateTime.fromISO(record.acquiredAt as string) : null, status: record.status as string, - photoCid: photoCids[0] ?? null, photos: photoCids.length > 0 ? photoCids : null, recordCreatedAt: record.createdAt ? DateTime.fromISO(record.createdAt as string) : null, indexedAt: DateTime.now(), diff --git a/tests/functional/explore/index.spec.ts b/tests/functional/explore/index.spec.ts index 9718ac2..017e79e 100644 --- a/tests/functional/explore/index.spec.ts +++ b/tests/functional/explore/index.spec.ts @@ -21,7 +21,7 @@ test.group('ExploreController.index', (group) => { status: 'done', name: 'HG Aerial', grade: 'HG', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() const response = await client.visit('explore').withInertia() @@ -56,7 +56,7 @@ test.group('ExploreController.index', (group) => { did: 'did:plc:amuro', name: 'HG Aerial', status: 'done', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() const response = await client.visit('explore').withInertia() @@ -166,7 +166,7 @@ test.group('ExploreController.index', (group) => { await KitFactory.merge({ did: 'did:plc:amuro', status: 'done', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() const response = await client.visit('explore').withInertia() @@ -185,7 +185,7 @@ test.group('ExploreController.index', (group) => { const kit = await KitFactory.merge({ did: 'did:plc:amuro', status: 'done', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() await KitLikeFactory.merge({ kitId: kit.id, did: 'did:plc:liker' }).create() swap(SilentAuthMiddleware, new FakeAuthMiddlewareWithUser('did:plc:liker')) @@ -205,7 +205,7 @@ test.group('ExploreController.index', (group) => { const kit = await KitFactory.merge({ did: 'did:plc:amuro', status: 'done', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() await KitLikeFactory.merge({ kitId: kit.id, did: 'did:plc:other' }).create() swap(SilentAuthMiddleware, new FakeAuthMiddlewareWithUser('did:plc:viewer')) diff --git a/tests/functional/feed/index.spec.ts b/tests/functional/feed/index.spec.ts index c6ff0f0..49db06c 100644 --- a/tests/functional/feed/index.spec.ts +++ b/tests/functional/feed/index.spec.ts @@ -16,7 +16,7 @@ test.group('FeedController.index', (group) => { did: 'did:plc:amuro', name: 'HG Aerial', status: 'done', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() const response = await client.visit('home').withInertia() @@ -42,7 +42,7 @@ test.group('FeedController.index', (group) => { did: 'did:plc:amuro', name: 'HG Aerial', status: 'done', - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], }).create() const response = await client.visit('home').withInertia() diff --git a/tests/functional/kits.spec.ts b/tests/functional/kits.spec.ts index 2e8b780..64d1aa6 100644 --- a/tests/functional/kits.spec.ts +++ b/tests/functional/kits.spec.ts @@ -55,7 +55,7 @@ test.group('Kits Management', (group) => { }) }) - test('validates and creates a new kit with an uploaded photo', async ({ client, db, swap }) => { + test('validates and creates a new kit with an uploaded photo', async ({ client, db, swap, assert }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService()) @@ -81,14 +81,16 @@ test.group('Kits Management', (group) => { name: 'RG Hi-Nu Gundam', grade: 'RG', status: 'in-progress', - photo_cid: 'bafyfakephotocid', }) + const kit1 = await Kit.query().where('name', 'RG Hi-Nu Gundam').firstOrFail() + assert.deepEqual(kit1.photos, ['bafyfakephotocid']) }) test('validates and creates a new kit with single uploaded photo using photos[]', async ({ client, db, swap, + assert, }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService()) @@ -111,16 +113,16 @@ test.group('Kits Management', (group) => { response.assertStatus(302) response.assertHeader('location', '/shelf') - await db.assertHas('kits', { - name: 'MG Nu Gundam', - photo_cid: 'bafyfakephotocid0', - }) + await db.assertHas('kits', { name: 'MG Nu Gundam' }) + const kit2 = await Kit.query().where('name', 'MG Nu Gundam').firstOrFail() + assert.deepEqual(kit2.photos, ['bafyfakephotocid0']) }) test('validates and creates a new kit with multiple uploaded photos', async ({ client, db, swap, + assert, }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService()) @@ -151,8 +153,9 @@ test.group('Kits Management', (group) => { name: 'MG Sazabi Ver Ka', grade: 'MG', status: 'in-progress', - photo_cid: 'bafyfakephotocid0', }) + const kit3 = await Kit.query().where('name', 'MG Sazabi Ver Ka').firstOrFail() + assert.deepEqual(kit3.photos, ['bafyfakephotocid0', 'bafyfakephotocid1']) }) test('fails validation when submitting an invalid acquiredAt date', async ({ diff --git a/tests/functional/sessions/create.spec.ts b/tests/functional/sessions/create.spec.ts index 895209b..6b754b6 100644 --- a/tests/functional/sessions/create.spec.ts +++ b/tests/functional/sessions/create.spec.ts @@ -15,7 +15,7 @@ test.group('SessionsController.create', (group) => { test('login page renders kit with transformer-shaped fields', async ({ client }) => { await KitFactory.merge({ - photoCid: 'bafyfakecid', + photos: ['bafyfakecid'], name: 'HG Aerial', grade: 'HG', scale: '1/144', diff --git a/tests/functional/stats/index.spec.ts b/tests/functional/stats/index.spec.ts index e431876..7246bc2 100644 --- a/tests/functional/stats/index.spec.ts +++ b/tests/functional/stats/index.spec.ts @@ -21,24 +21,21 @@ test.group('StatsController.index', (group) => { }) }) - test('accurately counts photos across multi-photo and legacy photo_cid kits', async ({ + test('accurately counts photos across kits with single and multi-photo arrays', async ({ client, }) => { - // 1 kit with legacy photoCid only -> 1 photo + // 1 kit with single photo await KitFactory.merge({ - photoCid: 'bafkreia1', - photos: null, + photos: ['bafkreia1'], }).create() // 1 kit with multi-photo array (3 photos) await KitFactory.merge({ - photoCid: 'bafkreia2', photos: ['bafkreia2', 'bafkreia3', 'bafkreia4'], }).create() // 1 kit with no photos await KitFactory.merge({ - photoCid: null, photos: null, }).create() diff --git a/tests/helpers/fake_kits_service.ts b/tests/helpers/fake_kits_service.ts index 5ce23f1..ca64f05 100644 --- a/tests/helpers/fake_kits_service.ts +++ b/tests/helpers/fake_kits_service.ts @@ -40,7 +40,6 @@ export class FakeKitsService extends KitsService { if (Array.isArray(data.photos)) { data.photos.forEach((_, i) => photoCids.push(`bafyfakephotocid${i}`)) } - const photoCid = photoCids[0] ?? null const uri = AtUri.make('did:plc:test', 'build.gunpla.kit', 'new-aerial').toString() await Kit.create({ @@ -51,7 +50,6 @@ export class FakeKitsService extends KitsService { scale: data.scale ?? null, manufacturer: data.manufacturer ?? null, status: data.status, - photoCid, photos: photoCids.length > 0 ? photoCids : null, indexedAt: DateTime.now(), })