Files
zpan/shared/types/index.ts
T
Jasper Van 2dca9d9ffb feat(db): image hosting schema, apiKey plugin, migration 0011 (v2.4.0 T1) (#315)
* feat(db): image hosting schema, apiKey plugin, migration 0011

Adds the DB infrastructure for the v2.4 image hosting feature:

- server/db/schema.ts: new `image_hosting_configs` (per-org singleton,
  custom domain + referer allowlist) and `image_hostings` tables with
  all required indexes and cascade-delete FKs
- server/db/auth-schema.ts: `apikey` table for @better-auth/api-key
  plugin (referenceId-indexed, rate-limiting fields, permissions column)
- server/auth.ts: enable apiKey plugin (references='organization',
  image-hosting:upload permission declared)
- shared/types/index.ts: ImageHostingConfig, ImageHosting, ImageHostingStatus
- migrations/0011_image-hosting.sql: generated by drizzle-kit (NOT
  hand-authored); creates all three new tables + indexes
- migrations/meta/0011_snapshot.json: baseline snapshot for future
  migration generation (repo was missing snapshots 0002–0010)
- package.json: add @better-auth/api-key ^1.6.2 dep, bump
  better-auth to ^1.6.2

Note: migration is tagged 0011_image-hosting (idx=11 in journal) rather
than 0012 because the existing 0011_notifications was registered as idx=10
(pre-existing numbering offset from a skipped 0006_ slot).

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: resolve coverage gap and migration filename collision

- Add getTableConfig FK reference tests to cover deferred .references()
  callbacks in schema.ts (fixes Codecov patch coverage < 94.98%)
- Rename 0011_image-hosting → 0012_image-hosting to avoid filename
  collision with pre-existing 0011_notifications migration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 03:18:52 -04:00

214 lines
4.0 KiB
TypeScript

import type { DirType, ObjectStatus, StorageMode, StorageStatus } from '../constants'
export interface StorageObject {
id: string
orgId: string
alias: string
name: string
type: string
size: number
dirtype: DirType
parent: string
object: string
storageId: string
status: ObjectStatus
createdAt: string
updatedAt: string
}
export interface Storage {
id: string
uid: string
title: string
mode: StorageMode
bucket: string
endpoint: string
region: string
accessKey: string
secretKey: string
customHost: string
capacity: number
used: number
status: StorageStatus
createdAt: string
updatedAt: string
}
export interface OrgQuota {
id: string
orgId: string
quota: number
used: number
}
export interface Organization {
id: string
name: string
slug: string
logo: string | null
metadata: string | null
createdAt: string
updatedAt: string | null
}
export interface Member {
id: string
organizationId: string
userId: string
role: string
createdAt: string
}
export interface Invitation {
id: string
organizationId: string
email: string
role: string
status: 'pending' | 'accepted' | 'rejected' | 'canceled'
expiresAt: string
inviterId: string
createdAt: string
}
export interface SystemOption {
key: string
value: string
public: boolean
}
export interface AuthProvider {
providerId: string
type: string
name: string
icon: string
}
export interface PaginatedResponse<T> {
items: T[]
total: number
page: number
pageSize: number
}
import type { ShareKind as _ShareKind } from '../schemas/share'
export type { ShareKind } from '../schemas/share'
// passwordHash is intentionally not part of the shared wire type; it never leaves the server.
export interface Share {
id: string
token: string
kind: _ShareKind
matterId: string
orgId: string
creatorId: string
expiresAt: string | null
downloadLimit: number | null
views: number
downloads: number
status: 'active' | 'revoked'
createdAt: string
}
export interface ShareRecipient {
id: string
shareId: string
recipientUserId: string | null
recipientEmail: string | null
createdAt: string
}
export interface ShareMatter {
name: string
type: string
dirtype: number
}
export interface ShareListItem extends Share {
matter: ShareMatter
recipientCount: number
}
export interface ShareView {
token: string
kind: _ShareKind
status: 'active' | 'revoked'
expiresAt: string | null
downloadLimit: number | null
matter: { name: string; type: string; size: number; isFolder: boolean }
creatorName: string
requiresPassword: boolean
expired: boolean
exhausted: boolean
accessibleByUser: boolean
downloads: number
views: number
rootRef: string
id?: string
matterId?: string
orgId?: string
creatorId?: string
createdAt?: string
recipients?: ShareRecipient[]
}
export interface Notification {
id: string
userId: string
type: string
title: string
body: string
refType: string | null
refId: string | null
metadata: string | null
readAt: string | null
createdAt: string
}
export interface ActivityEvent {
id: string
orgId: string
userId: string
action: string
targetType: string
targetId: string | null
targetName: string
metadata: string | null
createdAt: string
user: {
id: string
name: string
image: string | null
}
}
export interface ImageHostingConfig {
orgId: string
customDomain: string | null
cfHostnameId: string | null
domainVerifiedAt: string | null
refererAllowlist: string | null // JSON array of strings; null/empty => allow all
createdAt: string
updatedAt: string
}
export type ImageHostingStatus = 'draft' | 'active'
export interface ImageHosting {
id: string
orgId: string
token: string
path: string
storageId: string
storageKey: string
size: number
mime: string
width: number | null
height: number | null
status: ImageHostingStatus
accessCount: number
lastAccessedAt: string | null
createdAt: string
}