mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 06:09:41 +02:00
Extract feedback, analytics, and AI modules from mana-core-auth into standalone mana-analytics service (Hono + Bun, Port 3064). New service (services/mana-analytics/): - User feedback CRUD with voting - AI-powered feedback title generation via mana-llm - Simplified from DuckDB analytics to pure PostgreSQL - ~550 LOC Removed from mana-core-auth: - feedback/ module (6 files) - analytics/ module (4 files) - ai/ module (3 files) - db/schema/feedback.schema.ts mana-core-auth now contains ONLY pure auth: - Better Auth (JWT, Sessions, 2FA, Passkeys, OIDC, Magic Links) - Organizations/Guilds (membership management) - API Keys, Security, Me (GDPR), Health, Metrics - Ready for Phase 5: Hono rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
2 KiB
TypeScript
74 lines
2 KiB
TypeScript
import {
|
|
pgSchema,
|
|
uuid,
|
|
text,
|
|
timestamp,
|
|
integer,
|
|
boolean,
|
|
jsonb,
|
|
index,
|
|
unique,
|
|
pgEnum,
|
|
} from 'drizzle-orm/pg-core';
|
|
|
|
export const feedbackSchema = pgSchema('feedback');
|
|
|
|
export const feedbackCategoryEnum = pgEnum('feedback_category', [
|
|
'bug',
|
|
'feature',
|
|
'improvement',
|
|
'question',
|
|
'praise',
|
|
'other',
|
|
]);
|
|
|
|
export const feedbackStatusEnum = pgEnum('feedback_status', [
|
|
'new',
|
|
'reviewed',
|
|
'planned',
|
|
'in_progress',
|
|
'done',
|
|
'rejected',
|
|
]);
|
|
|
|
export const userFeedback = feedbackSchema.table(
|
|
'user_feedback',
|
|
{
|
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
userId: text('user_id').notNull(),
|
|
appId: text('app_id').notNull(),
|
|
title: text('title'),
|
|
feedbackText: text('feedback_text').notNull(),
|
|
category: feedbackCategoryEnum('category').default('other').notNull(),
|
|
status: feedbackStatusEnum('status').default('new').notNull(),
|
|
isPublic: boolean('is_public').default(true).notNull(),
|
|
adminResponse: text('admin_response'),
|
|
voteCount: integer('vote_count').default(0).notNull(),
|
|
deviceInfo: jsonb('device_info'),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
|
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
|
|
},
|
|
(table) => ({
|
|
userIdIdx: index('feedback_user_id_idx').on(table.userId),
|
|
appIdIdx: index('feedback_app_id_idx').on(table.appId),
|
|
statusIdx: index('feedback_status_idx').on(table.status),
|
|
})
|
|
);
|
|
|
|
export const feedbackVotes = feedbackSchema.table(
|
|
'feedback_votes',
|
|
{
|
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
feedbackId: uuid('feedback_id')
|
|
.notNull()
|
|
.references(() => userFeedback.id, { onDelete: 'cascade' }),
|
|
userId: text('user_id').notNull(),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
|
},
|
|
(table) => ({
|
|
feedbackUserUnique: unique('feedback_votes_unique').on(table.feedbackId, table.userId),
|
|
})
|
|
);
|
|
|
|
export type Feedback = typeof userFeedback.$inferSelect;
|
|
export type FeedbackVote = typeof feedbackVotes.$inferSelect;
|