feat(sdk): consolidate SDK functionality and update dependencies

- Introduced a new SDK package to replace the previous ssr-sdk, consolidating shared functionality for handling views and reactions.
- Updated package.json files across applications to reference the new @afilmory/sdk.
- Removed the old ssr-sdk package and its associated files, streamlining the codebase.
- Enhanced API routes for reactions and views to utilize the new SDK schemas and client methods.
- Updated dependencies in various packages to ensure compatibility and improved functionality.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-11-08 21:13:02 +08:00
parent d01da030e3
commit 8d0e5dfe6b
25 changed files with 1302 additions and 265 deletions

View File

@@ -22,6 +22,7 @@
"@afilmory/env": "workspace:*",
"@afilmory/framework": "workspace:*",
"@afilmory/redis": "workspace:*",
"@afilmory/sdk": "workspace:*",
"@afilmory/task-queue": "workspace:*",
"@afilmory/utils": "workspace:*",
"@aws-sdk/client-s3": "3.921.0",

View File

@@ -1,14 +1,18 @@
import { Body, Controller, Get, Post, Query } from '@afilmory/framework'
import { BizException, ErrorCode } from 'core/errors'
import { Body, Controller, createZodSchemaDto, Get, Post, Query } from '@afilmory/framework'
import type { AnalysisResponse } from '@afilmory/sdk'
import { AnalysisDtoSchema, ReactionDtoSchema } from '@afilmory/sdk'
import { ReactionDto } from './reaction.dto'
import { ReactionService } from './reaction.service'
class ReactionDto extends createZodSchemaDto(ReactionDtoSchema) {}
class AnalysisDto extends createZodSchemaDto(AnalysisDtoSchema) {}
@Controller('reactions')
export class ReactionController {
constructor(private readonly reactionService: ReactionService) {}
@Post('/')
@Post('/add')
async addReaction(@Body() body: ReactionDto) {
const { refKey, reaction } = body
@@ -16,35 +20,13 @@ export class ReactionController {
}
@Get('/')
async getReactions(@Query() query: { refKey?: string }): Promise<
Array<{
id: string
refKey: string
reaction: string
createdAt: string
}>
> {
const { refKey } = query
if (!refKey) {
throw new BizException(ErrorCode.COMMON_BAD_REQUEST, {
message: 'refKey query parameter is required',
})
}
return await this.reactionService.getReactionsByRefKey(refKey)
async getReactions(@Query() query: AnalysisDto): Promise<AnalysisResponse> {
return await this.reactionService.getReactionAnalysis(query.refKey)
}
@Get('/stats')
async getReactionStats(@Query() query: { refKey?: string }): Promise<Record<string, number>> {
const { refKey } = query
if (!refKey) {
throw new BizException(ErrorCode.COMMON_BAD_REQUEST, {
message: 'refKey query parameter is required',
})
}
return await this.reactionService.getReactionStats(refKey)
async getReactionStats(@Query() query: AnalysisDto): Promise<Record<string, number>> {
const analysis = await this.reactionService.getReactionAnalysis(query.refKey)
return analysis.data.reactions
}
}

View File

@@ -1,8 +0,0 @@
import { z } from 'zod'
export const ReactionDto = z.object({
refKey: z.string(),
reaction: z.string().min(1).max(20),
})
export type ReactionDto = z.infer<typeof ReactionDto>

View File

@@ -1,7 +1,8 @@
import { reactions } from '@afilmory/db'
import type { AnalysisResponse } from '@afilmory/sdk'
import { BizException, ErrorCode } from 'core/errors'
import { requireTenantContext } from 'core/modules/tenant/tenant.context'
import { eq } from 'drizzle-orm'
import { and, eq } from 'drizzle-orm'
import { injectable } from 'tsyringe'
import { DbAccessor } from '../../database/database.provider'
@@ -31,33 +32,8 @@ export class ReactionService {
}
}
async getReactionsByRefKey(refKey: string): Promise<
Array<{
id: string
refKey: string
reaction: string
createdAt: string
}>
> {
requireTenantContext()
const db = this.dbAccessor.get()
const records = await db
.select({
id: reactions.id,
refKey: reactions.refKey,
reaction: reactions.reaction,
createdAt: reactions.createdAt,
})
.from(reactions)
.where(eq(reactions.refKey, refKey))
.orderBy(reactions.createdAt)
return records
}
async getReactionStats(refKey: string): Promise<Record<string, number>> {
requireTenantContext()
async getReactionAnalysis(refKey: string): Promise<AnalysisResponse> {
const tenant = requireTenantContext()
const db = this.dbAccessor.get()
const reactionRecords = await db
@@ -65,14 +41,21 @@ export class ReactionService {
reaction: reactions.reaction,
})
.from(reactions)
.where(eq(reactions.refKey, refKey))
.where(and(eq(reactions.refKey, refKey), eq(reactions.tenantId, tenant.tenant.id)))
return reactionRecords.reduce(
const aggregated = reactionRecords.reduce(
(acc, record) => {
acc[record.reaction] = (acc[record.reaction] || 0) + 1
return acc
},
{} as Record<string, number>,
)
return {
data: {
view: 0,
reactions: aggregated,
},
}
}
}

View File

@@ -0,0 +1 @@
ALTER TABLE "reactions" DROP CONSTRAINT IF EXISTS "uq_reactions_tenant_ref_key";

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,13 @@
"when": 1762353600000,
"tag": "0006_remove_tenant_domains",
"breakpoints": true
},
{
"idx": 7,
"version": "7",
"when": 1762454400000,
"tag": "0007_allow_multiple_reactions",
"breakpoints": true
}
]
}

View File

@@ -213,10 +213,7 @@ export const reactions = pgTable(
refKey: text('ref_key').notNull(),
reaction: text('reaction').notNull(),
},
(t) => [
index('idx_reactions_tenant_ref_key').on(t.tenantId, t.refKey),
unique('uq_reactions_tenant_ref_key').on(t.tenantId, t.refKey),
],
(t) => [index('idx_reactions_tenant_ref_key').on(t.tenantId, t.refKey)],
)
export const photoAssets = pgTable(