mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-01 05:44:38 +08:00
chore: strengthen infrastructure quality gates
- Pre-commit: add typecheck before lint-staged (catches type errors locally) - Shared types: add StorageStatus constant, fix Storage.status type (string not number) - Delete duplicate web/src/types/storage.ts, import from @zpan/shared - Use StorageStatus.ACTIVE constant instead of magic string - CLAUDE.md: document Hono RPC requirement, shared types rule, updated hook description Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
pnpm lint-staged
|
||||
pnpm typecheck && pnpm lint-staged
|
||||
|
||||
@@ -25,4 +25,25 @@ Conventional Commits (`feat:`, `fix:`, `docs:`, etc.). PRs target `master`.
|
||||
|
||||
## Pre-commit Hooks
|
||||
|
||||
Husky + lint-staged run biome auto-fix on every `git commit`. **Never** bypass with `--no-verify`. **Never** run `pnpm install --ignore-scripts` — the `prepare` script must run so hooks are installed. If a hook fails, fix the underlying lint error and re-commit.
|
||||
Husky runs `pnpm typecheck` + lint-staged (biome auto-fix) on every `git commit`. **Never** bypass with `--no-verify`. **Never** run `pnpm install --ignore-scripts` — the `prepare` script must run so hooks are installed. If a hook fails, fix the underlying issue and re-commit.
|
||||
|
||||
## API Client (Hono RPC)
|
||||
|
||||
The frontend **must** use Hono RPC client for all API calls. **Never** use raw `fetch()` with hardcoded URL strings.
|
||||
|
||||
```typescript
|
||||
// ✅ Correct — type-safe, compile-time path validation
|
||||
import { hc } from 'hono/client'
|
||||
import type { AppType } from '@zpan/server/app'
|
||||
const client = hc<AppType>('/')
|
||||
const res = await client.api.admin.storages.$get()
|
||||
|
||||
// ❌ Wrong — hardcoded path, no type safety
|
||||
const res = await fetch('/api/admin/storages')
|
||||
```
|
||||
|
||||
Exception: `uploadToS3()` calls external S3 presigned URLs, not our API — raw `fetch` is OK there.
|
||||
|
||||
## Types
|
||||
|
||||
All shared types live in `packages/shared`. **Never** create duplicate type definitions in `packages/web` or `packages/server`. Import from `@zpan/shared/types` and `@zpan/shared/constants`.
|
||||
|
||||
@@ -76,7 +76,7 @@ function makeStorage(overrides: Partial<Storage> = {}): Storage {
|
||||
secretKey: 'SECRET',
|
||||
filePath: '$UID/$RAW_NAME$RAW_EXT',
|
||||
customHost: '',
|
||||
status: 1,
|
||||
status: 'active',
|
||||
createdAt: '2026-01-01',
|
||||
updatedAt: '2026-01-01',
|
||||
...overrides,
|
||||
|
||||
@@ -20,6 +20,13 @@ export const DirType = {
|
||||
|
||||
export type DirType = (typeof DirType)[keyof typeof DirType]
|
||||
|
||||
export const StorageStatus = {
|
||||
ACTIVE: 'active',
|
||||
INACTIVE: 'inactive',
|
||||
} as const
|
||||
|
||||
export type StorageStatus = (typeof StorageStatus)[keyof typeof StorageStatus]
|
||||
|
||||
export const ObjectStatus = {
|
||||
DRAFT: 'draft',
|
||||
ACTIVE: 'active',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DirType, ObjectStatus, StorageMode } from '../constants'
|
||||
import type { DirType, ObjectStatus, StorageMode, StorageStatus } from '../constants'
|
||||
|
||||
export interface StorageObject {
|
||||
id: string
|
||||
@@ -28,7 +28,7 @@ export interface Storage {
|
||||
secretKey: string
|
||||
filePath: string
|
||||
customHost: string
|
||||
status: number
|
||||
status: StorageStatus
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import type { Storage } from '@zpan/shared/types'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
@@ -11,7 +12,6 @@ import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import type { Storage } from '@/types/storage'
|
||||
|
||||
const storageFormSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import type { Storage } from '@zpan/shared/types'
|
||||
import { Database, Pencil, Plus, Trash2 } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DeleteStorageDialog } from '@/components/admin/delete-storage-dialog'
|
||||
import { StorageFormDialog } from '@/components/admin/storage-form-dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import type { Storage } from '@/types/storage'
|
||||
|
||||
export const Route = createFileRoute('/_authenticated/admin/storages/')({
|
||||
component: StoragesPage,
|
||||
})
|
||||
|
||||
const STORAGE_STATUS_ACTIVE = 'active'
|
||||
import { StorageStatus } from '@zpan/shared/constants'
|
||||
|
||||
function StoragesPage() {
|
||||
const { t } = useTranslation()
|
||||
@@ -124,7 +124,7 @@ function StorageTableRow({
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isActive = storage.status === STORAGE_STATUS_ACTIVE
|
||||
const isActive = storage.status === StorageStatus.ACTIVE
|
||||
|
||||
const modeBadge =
|
||||
storage.mode === 'public' ? 'bg-green-500/10 text-green-700 dark:text-green-400' : 'bg-primary/10 text-primary'
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
export interface Storage {
|
||||
id: string
|
||||
uid: string
|
||||
title: string
|
||||
mode: 'private' | 'public'
|
||||
bucket: string
|
||||
endpoint: string
|
||||
region: string
|
||||
accessKey: string
|
||||
secretKey: string
|
||||
filePath: string
|
||||
customHost: string
|
||||
status: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
Reference in New Issue
Block a user