mirror of
https://github.com/Afilmory/afilmory
synced 2026-04-24 23:05:05 +00:00
feat: implement comments feature (#171)
Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
36
be/packages/db/migrations/0010_wise_doorman.sql
Normal file
36
be/packages/db/migrations/0010_wise_doorman.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
CREATE TYPE "public"."comment_status" AS ENUM('pending', 'approved', 'rejected', 'hidden');--> statement-breakpoint
|
||||
CREATE TABLE "comment_reaction" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"tenant_id" text NOT NULL,
|
||||
"comment_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"reaction" text NOT NULL,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_comment_reaction_user" UNIQUE("tenant_id","comment_id","user_id","reaction")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "comment" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"tenant_id" text NOT NULL,
|
||||
"photo_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"parent_id" text,
|
||||
"content" text NOT NULL,
|
||||
"status" "comment_status" DEFAULT 'approved' NOT NULL,
|
||||
"user_agent" text,
|
||||
"client_ip" text,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "photo_asset" ALTER COLUMN "manifest_version" SET DEFAULT 'v9';--> statement-breakpoint
|
||||
ALTER TABLE "comment_reaction" ADD CONSTRAINT "comment_reaction_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "comment_reaction" ADD CONSTRAINT "comment_reaction_comment_id_comment_id_fk" FOREIGN KEY ("comment_id") REFERENCES "public"."comment"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "comment_reaction" ADD CONSTRAINT "comment_reaction_user_id_auth_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "comment" ADD CONSTRAINT "comment_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "comment" ADD CONSTRAINT "comment_user_id_auth_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "idx_comment_reaction_comment" ON "comment_reaction" USING btree ("tenant_id","comment_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_comment_tenant_photo" ON "comment" USING btree ("tenant_id","photo_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_comment_parent" ON "comment" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_comment_user" ON "comment" USING btree ("user_id");
|
||||
2087
be/packages/db/migrations/meta/0010_snapshot.json
Normal file
2087
be/packages/db/migrations/meta/0010_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -71,6 +71,13 @@
|
||||
"when": 1764070049538,
|
||||
"tag": "0009_stormy_sway",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "7",
|
||||
"when": 1764154685207,
|
||||
"tag": "0010_wise_doorman",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { PhotoManifestItem } from '@afilmory/builder'
|
||||
import type { ManifestVersion } from '@afilmory/builder/manifest/version'
|
||||
import { CURRENT_MANIFEST_VERSION } from '@afilmory/builder/manifest/version'
|
||||
import type { ManifestVersion } from '@afilmory/builder/manifest/version.js'
|
||||
import { CURRENT_MANIFEST_VERSION } from '@afilmory/builder/manifest/version.ts'
|
||||
import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
bigint,
|
||||
boolean,
|
||||
@@ -31,6 +32,7 @@ export const userRoleEnum = pgEnum('user_role', ['user', 'admin', 'superadmin'])
|
||||
export const tenantStatusEnum = pgEnum('tenant_status', ['active', 'inactive', 'suspended'])
|
||||
export const tenantDomainStatusEnum = pgEnum('tenant_domain_status', ['pending', 'verified', 'disabled'])
|
||||
export const photoSyncStatusEnum = pgEnum('photo_sync_status', ['pending', 'synced', 'conflict'])
|
||||
export const commentStatusEnum = pgEnum('comment_status', ['pending', 'approved', 'rejected', 'hidden'])
|
||||
export const CURRENT_PHOTO_MANIFEST_VERSION: ManifestVersion = CURRENT_MANIFEST_VERSION
|
||||
|
||||
export type PhotoAssetConflictType = 'missing-in-storage' | 'metadata-mismatch' | 'photo-id-conflict'
|
||||
@@ -228,6 +230,63 @@ export const reactions = pgTable(
|
||||
(t) => [index('idx_reactions_tenant_ref_key').on(t.tenantId, t.refKey)],
|
||||
)
|
||||
|
||||
export const comments = pgTable(
|
||||
'comment',
|
||||
{
|
||||
id: snowflakeId,
|
||||
tenantId: text('tenant_id')
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: 'cascade' }),
|
||||
photoId: text('photo_id').notNull(),
|
||||
userId: text('user_id')
|
||||
.notNull()
|
||||
.references(() => authUsers.id, { onDelete: 'cascade' }),
|
||||
parentId: text('parent_id'),
|
||||
content: text('content').notNull(),
|
||||
status: commentStatusEnum('status').notNull().default('approved'),
|
||||
userAgent: text('user_agent'),
|
||||
clientIp: text('client_ip'),
|
||||
createdAt: timestamp('created_at', { mode: 'string' }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { mode: 'string' }).defaultNow().notNull(),
|
||||
deletedAt: timestamp('deleted_at', { mode: 'string' }),
|
||||
},
|
||||
(t) => [
|
||||
index('idx_comment_tenant_photo').on(t.tenantId, t.photoId),
|
||||
index('idx_comment_parent').on(t.parentId),
|
||||
index('idx_comment_user').on(t.userId),
|
||||
],
|
||||
)
|
||||
|
||||
export const commentsRelations = relations(comments, ({ one, many }) => ({
|
||||
parent: one(comments, {
|
||||
fields: [comments.parentId],
|
||||
references: [comments.id],
|
||||
}),
|
||||
children: many(comments),
|
||||
}))
|
||||
|
||||
export const commentReactions = pgTable(
|
||||
'comment_reaction',
|
||||
{
|
||||
id: snowflakeId,
|
||||
tenantId: text('tenant_id')
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: 'cascade' }),
|
||||
commentId: text('comment_id')
|
||||
.notNull()
|
||||
.references(() => comments.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id')
|
||||
.notNull()
|
||||
.references(() => authUsers.id, { onDelete: 'cascade' }),
|
||||
reaction: text('reaction').notNull(),
|
||||
createdAt: timestamp('created_at', { mode: 'string' }).defaultNow().notNull(),
|
||||
},
|
||||
(t) => [
|
||||
unique('uq_comment_reaction_user').on(t.tenantId, t.commentId, t.userId, t.reaction),
|
||||
index('idx_comment_reaction_comment').on(t.tenantId, t.commentId),
|
||||
],
|
||||
)
|
||||
|
||||
export const managedStorageUsages = pgTable(
|
||||
'managed_storage_usage',
|
||||
{
|
||||
@@ -411,6 +470,8 @@ export const dbSchema = {
|
||||
settings,
|
||||
systemSettings,
|
||||
reactions,
|
||||
comments,
|
||||
commentReactions,
|
||||
managedStorageUsages,
|
||||
managedStorageFileReferences,
|
||||
photoAssets,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"sourceMap": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
@@ -30,7 +30,7 @@ export type LoggerContextProvider = () => LoggerContextValue | LoggerContextValu
|
||||
|
||||
const globalContextProviders = new Set<LoggerContextProvider>()
|
||||
|
||||
function toContextStrings (value: LoggerContextValue | LoggerContextValue[] | undefined): string[] {
|
||||
function toContextStrings(value: LoggerContextValue | LoggerContextValue[] | undefined): string[] {
|
||||
if (Array.isArray(value)) {
|
||||
return value.flatMap((item) => (typeof item === 'string' && item ? [item] : []))
|
||||
}
|
||||
@@ -38,7 +38,7 @@ function toContextStrings (value: LoggerContextValue | LoggerContextValue[] | un
|
||||
return typeof value === 'string' && value ? [value] : []
|
||||
}
|
||||
|
||||
function invokeContextProvider (provider: LoggerContextProvider): string[] {
|
||||
function invokeContextProvider(provider: LoggerContextProvider): string[] {
|
||||
try {
|
||||
return toContextStrings(provider())
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user