3 Commits

Author SHA1 Message Date
Eamon-meng
ea3e7e8afd 发布v1.0.0版本 2025-12-20 18:08:17 +08:00
adc8195d53 修复编译问题 & 将人机验证组件动态化 2025-12-20 18:05:46 +08:00
c979765a77 添加白名单上限提示 2025-12-20 16:08:20 +08:00
11 changed files with 23 additions and 78 deletions

3
.npmrc
View File

@@ -1,2 +1 @@
http-proxy=http://localhost:10808
registry=https://registry.npmmirror.com
registry=https://registry.npmmirror.com

View File

@@ -1,11 +1,10 @@
FROM oven/bun:1.2.19-alpine AS base
FROM oven/bun:1.3.2-alpine AS base
# 依赖缓存阶段
FROM base AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun config set registry https://registry.npmmirror.com
COPY package.json bun.lock .npmrc ./
RUN bun install --frozen-lockfile
# 构建阶段

View File

@@ -1,7 +1,5 @@
## TODO
提取记录,长效动态统计
购买页固定套餐
### 禁止直接依赖 form
@@ -10,6 +8,8 @@
### 次要
业务定制页面每月需求用量,可选项需要确认是否合理
页头高度降低
帮助中心文档优化

View File

@@ -1,6 +1,6 @@
{
"name": "portal",
"version": "0.1.0",
"name": "lanhu-web",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -H 0.0.0.0 --turbopack",

View File

@@ -2,9 +2,8 @@ import {NextRequest, NextResponse} from 'next/server'
import {createChannels} from '@/actions/channel'
export async function GET(req: NextRequest) {
const params = req.nextUrl.searchParams
try {
const params = req.nextUrl.searchParams
const resource_id = params.get('i')
if (!resource_id) {
throw new Error('需要指定资源ID')

View File

@@ -15,8 +15,7 @@ import {toast} from 'sonner'
import {useRouter} from 'next/navigation'
import {login, LoginMode} from '@/actions/auth'
import {useProfileStore} from '@/components/stores/profile'
import SendMsg from '@/components/send-msg'
import '@cap.js/widget'
import dynamic from 'next/dynamic'
const smsSchema = zod.object({
username: zod.string().length(11, '请输入正确的手机号码'),
@@ -211,3 +210,5 @@ function SendMsgByUsername() {
const phone = useWatch({control, name: 'username'})
return <SendMsg phone={phone}/>
}
const SendMsg = dynamic(() => import('@/components/send-msg'), {ssr: false})

View File

@@ -11,7 +11,7 @@ import actionBill from '../_assets/action-bill.webp'
import actionBuy from '../_assets/action-buy.webp'
import actionLogout from '../_assets/action-logout.webp'
async function UserCenter() {
export default async function UserCenter() {
const resp = await getProfile()
if (!resp.success) {
return (
@@ -90,4 +90,3 @@ async function UserCenter() {
</Card>
)
}
export default UserCenter

View File

@@ -1,4 +1,4 @@
import {ReactNode} from 'react'
import {ReactNode, Suspense} from 'react'
import {Metadata} from 'next'
export async function generateMetadata(): Promise<Metadata> {
@@ -12,5 +12,5 @@ export type ProfileLayoutProps = {
}
export default async function ProfileLayout(props: ProfileLayoutProps) {
return props.children
return <Suspense>{props.children}</Suspense>
}

View File

@@ -24,6 +24,7 @@ import Page from '@/components/page'
import DataTable from '@/components/data-table'
import {format, parseISO} from 'date-fns'
import {getClientIp} from '@/actions/ip'
import {Alert, AlertTitle} from '@/components/ui/alert'
const schema = z.object({
host: z.string().min(1, {message: 'IP地址不能为空'}),
remark: z.string().optional(),
@@ -241,10 +242,14 @@ export default function WhitelistPage(props: WhitelistPageProps) {
<Button onClick={() => openDialog('add')} disabled={wait || data.total >= MAX_WHITELIST_COUNT}>
<Plus/>
{data.total >= MAX_WHITELIST_COUNT && '(已达上限)'}
</Button>
</section>
{/* 全局提示 */}
<Alert>
<AlertTitle> {data.total}/{MAX_WHITELIST_COUNT}</AlertTitle>
</Alert>
{/* 数据表 */}
<DataTable
status={status}

View File

@@ -1,59 +0,0 @@
import {ReactNode} from 'react'
export type SftpayPageProps = {
}
export default async function SftpayPage(props: SftpayPageProps) {
return (
<div className="flex items-center justify-center min-h-screen bg-gray-100">
<div className="w-full max-w-md p-8 space-y-8 bg-white rounded-lg shadow-lg">
<div className="text-center">
<div className="flex justify-center">
<div className="rounded-full bg-green-100 p-3">
<svg className="h-12 w-12 text-green-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7"/>
</svg>
</div>
</div>
<h2 className="mt-6 text-3xl font-extrabold text-gray-900"></h2>
<p className="mt-2 text-sm text-gray-600">
使
</p>
</div>
<div className="mt-8 space-y-6">
<div className="border-t border-gray-200 pt-4">
<dl className="space-y-4">
<div className="flex justify-between">
<dt className="text-sm font-medium text-gray-500"></dt>
<dd className="text-sm text-gray-900">ORD-12345678</dd>
</div>
<div className="flex justify-between">
<dt className="text-sm font-medium text-gray-500"></dt>
<dd className="text-sm font-bold text-gray-900">¥ 299.00</dd>
</div>
<div className="flex justify-between">
<dt className="text-sm font-medium text-gray-500"></dt>
<dd className="text-sm text-gray-900">{new Date().toLocaleString('zh-CN')}</dd>
</div>
<div className="flex justify-between">
<dt className="text-sm font-medium text-gray-500"></dt>
<dd className="text-sm text-gray-900">SFT支付</dd>
</div>
</dl>
</div>
<div className="flex items-center justify-center">
<button
type="button"
className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
</button>
</div>
</div>
</div>
</div>
)
}

View File

@@ -10,7 +10,7 @@ import * as z from 'zod'
import {toast} from 'sonner'
import {useRouter} from 'next/navigation'
import {updatePassword} from '@/actions/user'
import SendMsg from '@/components/send-msg'
import dynamic from 'next/dynamic'
// 表单验证规则
const schema = z.object({
@@ -158,3 +158,5 @@ function SendMsgByPhone() {
const phone = useWatch({control, name: 'phone'})
return <SendMsg phone={phone}/>
}
const SendMsg = dynamic(() => import('@/components/send-msg'), {ssr: false})