diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..9fc721f --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,15 @@ +import tseslint from "typescript-eslint"; +import next from "@next/eslint-plugin-next"; + +export default [ + { ignores: [".next/**", "node_modules/**", "scripts/**", "next.config.js"] }, + ...tseslint.configs.recommended, + { + files: ["**/*.{js,jsx,ts,tsx}"], + plugins: { "@next/next": next }, + rules: { + ...next.configs.recommended.rules, + ...next.configs["core-web-vitals"].rules, + }, + }, +]; diff --git a/next-env.d.ts b/next-env.d.ts index 40c3d68..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.js b/next.config.js index 5722927..c10e07d 100644 --- a/next.config.js +++ b/next.config.js @@ -1,40 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone", - async headers() { - const isDev = process.env.NODE_ENV === "development" - const csp = [ - "default-src 'self'", - "base-uri 'self'", - "frame-ancestors 'none'", - "object-src 'none'", - "form-action 'self'", - "img-src 'self' data:", - isDev - ? "script-src 'self' 'unsafe-eval' 'unsafe-inline'" - : "script-src 'self'", - "style-src 'self' 'unsafe-inline'", - "connect-src 'self'", - ].join("; ") - - return [ - { - source: "/(.*)", - headers: [ - { key: "Content-Security-Policy", value: csp }, - { key: "X-Frame-Options", value: "DENY" }, - { key: "X-Content-Type-Options", value: "nosniff" }, - { key: "Referrer-Policy", value: "no-referrer" }, - { - key: "Strict-Transport-Security", - value: "max-age=63072000; includeSubDomains; preload", - }, - { key: "Permissions-Policy", value: "geolocation=(), microphone=(), camera=()" }, - ], - }, - ] - }, -} - -module.exports = nextConfig +}; +module.exports = nextConfig; diff --git a/package.json b/package.json index f15838c..71a893e 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,38 @@ { - "name": "cms-front", - "private": true, - "version": "0.1.0", - "scripts": { - "dev": "next dev -p 6031", - "build": "next build", - "start": "next start -p 6031", - "lint": "next lint" - }, - "dependencies": { - "@radix-ui/react-checkbox": "^1.3.0", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-slot": "^1.2.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "lucide-react": "^0.468.0", - "next": "^14.2.25", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "tailwind-merge": "^2.5.4" - }, - "devDependencies": { - "@types/node": "^20.17.16", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", - "autoprefixer": "^10.4.20", - "eslint": "^8.57.1", - "eslint-config-next": "^14.2.25", - "postcss": "^8.5.1", - "tailwindcss": "^3.4.17", - "typescript": "^5.7.3" - } -} \ No newline at end of file + "name": "cms-front", + "private": true, + "version": "0.1.0", + "scripts": { + "dev": "next dev --turbopack -p 6031", + "build": "next build", + "start": "next start -p 6031", + "lint": "eslint .", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@radix-ui/react-checkbox": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-slot": "^1.2.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.468.0", + "next": "^16.1.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "tailwind-merge": "^3.4.0" + }, + "devDependencies": { + "@next/eslint-plugin-next": "^16.1.6", + "@tailwindcss/postcss": "^4.1.18", + "@types/node": "^20.17.16", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "autoprefixer": "^10.4.20", + "eslint": "^9.39.2", + "eslint-config-next": "^16.1.6", + "postcss": "^8.5.1", + "tailwindcss": "^4.1.18", + "typescript": "^5.7.3", + "typescript-eslint": "^8.54.0" + } +} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 2ce518b..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} - diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..14502dc --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,6 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + autoprefixer: {}, + }, +} diff --git a/src/app/auth-error/page.tsx b/src/app/auth-error/page.tsx index 4d9bb91..cd53bd3 100644 --- a/src/app/auth-error/page.tsx +++ b/src/app/auth-error/page.tsx @@ -1,18 +1,21 @@ -export default function AuthErrorPage({ +import Link from "next/link"; + +export default async function AuthErrorPage({ searchParams, }: { - searchParams?: { message?: string } + searchParams: Promise<{ message?: string }> }) { - const message = searchParams?.message ?? "认证失败" + const sp = await searchParams; + const message = sp?.message ?? "认证失败" return (
无法完成登录
{message}
- + 返回首页重试 - +
diff --git a/src/app/globals.css b/src/app/globals.css index 4ec32f8..3453f03 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,6 +1,96 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; + +@custom-variant dark (&:where(.dark, .dark *)); + +@theme { + --color-border: hsl(var(--border)); + --color-input: hsl(var(--input)); + --color-ring: hsl(var(--ring)); + --color-background: hsl(var(--background)); + --color-foreground: hsl(var(--foreground)); + + --color-primary: hsl(var(--primary)); + --color-primary-foreground: hsl(var(--primary-foreground)); + + --color-secondary: hsl(var(--secondary)); + --color-secondary-foreground: hsl(var(--secondary-foreground)); + + --color-destructive: hsl(var(--destructive)); + --color-destructive-foreground: hsl(var(--destructive-foreground)); + + --color-muted: hsl(var(--muted)); + --color-muted-foreground: hsl(var(--muted-foreground)); + + --color-accent: hsl(var(--accent)); + --color-accent-foreground: hsl(var(--accent-foreground)); + + --color-card: hsl(var(--card)); + --color-card-foreground: hsl(var(--card-foreground)); + + --radius-lg: var(--radius); + --radius-md: calc(var(--radius) - 2px); + --radius-sm: calc(var(--radius) - 4px); + + /* Custom Container Max Width */ + --container-max-width: 1200px; +} + +/* Fluid Typography & Base Styles */ +@layer base { + html { + /* Mobile base size */ + font-size: 14px; + + /* Desktop base size (md breakpoint approx 768px) */ + @media (min-width: 768px) { + font-size: 16px; + } + } + + h1, h2, h3, h4, h5, h6 { + /* Fluid scaling using clamp/calc */ + font-size: clamp(1.25rem, 1rem + 1vw, 2.5rem); + line-height: 1.2; + } +} + +/* Utility for Grid System */ +@utility grid-responsive { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 1rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + @media (min-width: 1024px) { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } +} + +/* Utility for Responsive Content Container */ +@utility container-responsive { + width: 100%; + margin-left: auto; + margin-right: auto; + max-width: 1200px; + + /* Mobile Padding */ + padding-left: 1rem; /* 16px */ + padding-right: 1rem; + + @media (min-width: 768px) { + /* Tablet/Desktop Padding */ + padding-left: 2.5rem; /* 40px */ + padding-right: 2.5rem; + } + + @media (min-width: 1024px) { + padding-left: 3.75rem; /* 60px */ + padding-right: 3.75rem; + } +} :root { --background: 0 0% 100%; @@ -27,4 +117,3 @@ body { background: hsl(var(--background)); color: hsl(var(--foreground)); } - diff --git a/src/app/page.tsx b/src/app/page.tsx index 71ddcf4..88b4298 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,9 @@ import { cookies } from "next/headers" -export default function Home() { - const tenantId = cookies().get("tenantId")?.value ?? "" - const userId = cookies().get("userId")?.value ?? "" +export default async function Home() { + const cookieStore = await cookies() + const tenantId = cookieStore.get("tenantId")?.value ?? "" + const userId = cookieStore.get("userId")?.value ?? "" return (
diff --git a/src/middleware.ts b/src/proxy.ts similarity index 98% rename from src/middleware.ts rename to src/proxy.ts index 362b61f..92529fa 100644 --- a/src/middleware.ts +++ b/src/proxy.ts @@ -19,7 +19,7 @@ function isExpired(jwt: string): boolean { } } -export function middleware(req: NextRequest) { +export function proxy(req: NextRequest) { if (process.env.NODE_ENV === "production") { const proto = req.headers.get("x-forwarded-proto"); if (proto && proto !== "https") { diff --git a/tailwind.config.ts b/tailwind.config.ts deleted file mode 100644 index fc85e23..0000000 --- a/tailwind.config.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Config } from "tailwindcss" - -const config: Config = { - darkMode: ["class"], - content: ["./src/**/*.{ts,tsx}"], - theme: { - container: { - center: true, - padding: "2rem", - screens: { - "2xl": "1400px", - }, - }, - extend: { - colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - }, - }, - plugins: [], -} - -export default config - diff --git a/tsconfig.json b/tsconfig.json index 48907b4..ac64d54 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,15 +11,24 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, - "plugins": [{ "name": "next" }], + "plugins": [ + { + "name": "next" + } + ], "baseUrl": ".", "paths": { "@/*": ["src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], "exclude": ["node_modules"] } -