generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } enum AccessMode { web download } enum ToolStatus { draft published archived } enum ArtifactStatus { active deprecated deleted } enum DownloadRecordStatus { success failed } enum AdminUserStatus { active disabled } model Tool { id String @id name String slug String @unique categoryId String @map("category_id") description String rating Float @default(0) downloadCount Int @default(0) @map("download_count") openCount Int @default(0) @map("open_count") accessMode AccessMode @default(download) @map("access_mode") openUrl String? @map("open_url") openInNewTab Boolean @default(true) @map("open_in_new_tab") latestArtifactId String? @map("latest_artifact_id") status ToolStatus @default(draft) updatedAt String @map("updated_at") isDeleted Boolean @default(false) @map("is_deleted") createdAt DateTime @default(now()) @map("created_at") modifiedAt DateTime @updatedAt @map("modified_at") category Category @relation(fields: [categoryId], references: [id], onDelete: Restrict) latestArtifact ToolArtifact? @relation("ToolLatestArtifact", fields: [latestArtifactId], references: [id], onDelete: SetNull) artifacts ToolArtifact[] @relation("ToolArtifacts") tags ToolTag[] features ToolFeature[] downloadTickets DownloadTicket[] downloadRecords DownloadRecord[] openRecords OpenRecord[] @@index([categoryId], map: "idx_tools_category_id") @@index([status], map: "idx_tools_status") @@index([accessMode], map: "idx_tools_access_mode") @@index([downloadCount], map: "idx_tools_download_count") @@index([openCount], map: "idx_tools_open_count") @@index([updatedAt], map: "idx_tools_updated_at") @@index([rating], map: "idx_tools_rating") @@index([name], map: "idx_tools_name") @@map("tools") } model ToolArtifact { id String @id toolId String @map("tool_id") version String fileName String @map("file_name") fileSizeBytes Int @map("file_size_bytes") sha256 String mimeType String? @map("mime_type") gitlabProjectId Int @map("gitlab_project_id") gitlabPackageName String @map("gitlab_package_name") gitlabPackageVersion String @map("gitlab_package_version") gitlabFilePath String @map("gitlab_file_path") status ArtifactStatus @default(active) releaseNotes String? @map("release_notes") uploadedBy String? @map("uploaded_by") createdAt DateTime @default(now()) @map("created_at") tool Tool @relation("ToolArtifacts", fields: [toolId], references: [id], onDelete: Restrict) latestForTool Tool[] @relation("ToolLatestArtifact") uploader AdminUser? @relation(fields: [uploadedBy], references: [id], onDelete: SetNull) downloadTickets DownloadTicket[] downloadRecords DownloadRecord[] @@unique([toolId, version], map: "uk_tool_version") @@index([toolId], map: "idx_artifact_tool_id") @@index([status], map: "idx_artifact_status") @@map("tool_artifacts") } model Category { id String @id name String @unique sortOrder Int @default(100) @map("sort_order") isDeleted Boolean @default(false) @map("is_deleted") tools Tool[] @@map("categories") } model Tag { id String @id name String @unique isDeleted Boolean @default(false) @map("is_deleted") tools ToolTag[] @@map("tags") } model ToolTag { toolId String @map("tool_id") tagId String @map("tag_id") tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade) tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@id([toolId, tagId]) @@map("tool_tags") } model ToolFeature { id String @id toolId String @map("tool_id") featureText String @map("feature_text") sortOrder Int @default(100) @map("sort_order") tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade) @@index([toolId], map: "idx_tool_feature_tool_id") @@map("tool_features") } model HotKeyword { id String @id keyword String @unique sortOrder Int @default(100) @map("sort_order") isActive Boolean @default(true) @map("is_active") @@map("hot_keywords") } model DownloadTicket { id Int @id @default(autoincrement()) ticket String @unique toolId String @map("tool_id") artifactId String @map("artifact_id") channel String? clientVersion String? @map("client_version") requestIp String? @map("request_ip") expiresAt DateTime @map("expires_at") consumedAt DateTime? @map("consumed_at") createdAt DateTime @default(now()) @map("created_at") tool Tool @relation(fields: [toolId], references: [id], onDelete: Restrict) artifact ToolArtifact @relation(fields: [artifactId], references: [id], onDelete: Restrict) @@index([toolId], map: "idx_download_tickets_tool_id") @@index([expiresAt], map: "idx_download_tickets_expires_at") @@map("download_tickets") } model DownloadRecord { id Int @id @default(autoincrement()) toolId String @map("tool_id") artifactId String @map("artifact_id") ticket String? downloadedAt DateTime @default(now()) @map("downloaded_at") clientIp String? @map("client_ip") userAgent String? @map("user_agent") channel String? clientVersion String? @map("client_version") status DownloadRecordStatus @default(success) errorMessage String? @map("error_message") tool Tool @relation(fields: [toolId], references: [id], onDelete: Restrict) artifact ToolArtifact @relation(fields: [artifactId], references: [id], onDelete: Restrict) @@index([toolId], map: "idx_download_records_tool_id") @@index([artifactId], map: "idx_download_records_artifact_id") @@map("download_records") } model OpenRecord { id Int @id @default(autoincrement()) toolId String @map("tool_id") openedAt DateTime @default(now()) @map("opened_at") clientIp String? @map("client_ip") userAgent String? @map("user_agent") channel String? clientVersion String? @map("client_version") referer String? tool Tool @relation(fields: [toolId], references: [id], onDelete: Restrict) @@index([toolId], map: "idx_open_records_tool_id") @@map("open_records") } model AdminUser { id String @id username String @unique passwordHash String @map("password_hash") displayName String? @map("display_name") status AdminUserStatus @default(active) lastLoginAt DateTime? @map("last_login_at") createdAt DateTime @default(now()) @map("created_at") modifiedAt DateTime @updatedAt @map("modified_at") artifacts ToolArtifact[] auditLogs AdminAuditLog[] @@map("admin_users") } model AdminAuditLog { id Int @id @default(autoincrement()) adminUserId String? @map("admin_user_id") action String resourceType String @map("resource_type") resourceId String? @map("resource_id") requestMethod String @map("request_method") requestPath String @map("request_path") requestBody String? @map("request_body") ip String? userAgent String? @map("user_agent") createdAt DateTime @default(now()) @map("created_at") adminUser AdminUser? @relation(fields: [adminUserId], references: [id], onDelete: SetNull) @@index([adminUserId], map: "idx_admin_audit_logs_user_id") @@map("admin_audit_logs") }