Initial commit to prepare for remote overwrite

This commit is contained in:
卡若
2026-01-09 11:57:57 +08:00
commit 924307a470
172 changed files with 16577 additions and 0 deletions

55
app/api/config/route.ts Normal file
View File

@@ -0,0 +1,55 @@
import { NextResponse } from 'next/server';
export async function GET() {
const config = {
paymentMethods: {
wechat: {
enabled: true,
qrCode: "/images/wechat-pay.png",
account: "卡若",
appId: process.env.TENCENT_APP_ID || "1251077262", // From .env or default
// 敏感信息后端处理,不完全暴露给前端
},
alipay: {
enabled: true,
qrCode: "/images/alipay.png",
account: "卡若",
appId: process.env.ALIPAY_ACCESS_KEY_ID || "LTAI5t9zkiWmFtHG8qmtdysW", // Using Access Key as placeholder ID
},
usdt: {
enabled: true,
network: "TRC20",
address: process.env.USDT_WALLET_ADDRESS || "TWeq9xxxxxxxxxxxxxxxxxxxx",
exchangeRate: 7.2
},
paypal: {
enabled: false,
email: process.env.PAYPAL_CLIENT_ID || "",
exchangeRate: 7.2
}
},
marketing: {
partyGroup: {
url: "https://soul.cn/party",
liveCodeUrl: "https://soul.cn/party-live",
qrCode: "/images/party-group-qr.png"
},
banner: {
text: "每日早上6-9点Soul派对房不见不散",
visible: true
}
},
authorInfo: {
name: "卡若",
description: "私域运营与技术公司主理人",
liveTime: "06:00-09:00",
platform: "Soul"
},
system: {
version: "1.0.0",
maintenance: false
}
};
return NextResponse.json(config);
}

36
app/api/content/route.ts Normal file
View File

@@ -0,0 +1,36 @@
import { type NextRequest, NextResponse } from "next/server"
import fs from "fs"
import path from "path"
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const filePath = searchParams.get("path")
if (!filePath) {
return NextResponse.json({ error: "Path is required" }, { status: 400 })
}
if (filePath.startsWith("custom/")) {
return NextResponse.json({ content: "", isCustom: true })
}
try {
const normalizedPath = filePath.replace(/^\/+/, "")
const fullPath = path.join(process.cwd(), normalizedPath)
if (!fs.existsSync(fullPath)) {
return NextResponse.json({ error: "File not found" }, { status: 404 })
}
const stats = fs.statSync(fullPath)
if (stats.isDirectory()) {
return NextResponse.json({ error: "Path is a directory" }, { status: 400 })
}
const content = fs.readFileSync(fullPath, "utf-8")
return NextResponse.json({ content, isCustom: false })
} catch (error) {
console.error("[v0] Error reading file:", error)
return NextResponse.json({ error: "Failed to read file" }, { status: 500 })
}
}

View File

@@ -0,0 +1,55 @@
import { NextResponse, type NextRequest } from "next/server"
import { getDocumentationCatalog } from "@/lib/documentation/catalog"
import { captureScreenshots } from "@/lib/documentation/screenshot"
import { renderDocumentationDocx } from "@/lib/documentation/docx"
export const runtime = "nodejs"
function getBaseUrl(request: NextRequest) {
const proto = request.headers.get("x-forwarded-proto") || "http"
const host = request.headers.get("x-forwarded-host") || request.headers.get("host")
if (!host) return null
return `${proto}://${host}`
}
function isAuthorized(request: NextRequest) {
const token = process.env.DOCUMENTATION_TOKEN
// If no token is configured, allow access (internal tool)
if (!token || token === "") return true
const header = request.headers.get("x-documentation-token")
const query = request.nextUrl.searchParams.get("token")
return header === token || query === token
}
export async function POST(request: NextRequest) {
if (!isAuthorized(request)) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}
const baseUrl = getBaseUrl(request)
if (!baseUrl) {
return NextResponse.json({ error: "Host is required" }, { status: 400 })
}
try {
const pages = getDocumentationCatalog()
const screenshots = await captureScreenshots(pages, {
baseUrl,
timeoutMs: 60000,
viewport: { width: 430, height: 932 },
})
const docxBuffer = await renderDocumentationDocx(screenshots)
return new NextResponse(docxBuffer, {
status: 200,
headers: {
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"Content-Disposition": `attachment; filename="app-documentation.docx"`,
"Cache-Control": "no-store",
},
})
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return NextResponse.json({ error: message }, { status: 500 })
}
}

12
app/api/menu/route.ts Normal file
View File

@@ -0,0 +1,12 @@
import { NextResponse } from "next/server"
import { getBookStructure } from "@/lib/book-file-system"
export async function GET() {
try {
const structure = getBookStructure()
return NextResponse.json(structure)
} catch (error) {
console.error("Error generating menu:", error)
return NextResponse.json({ error: "Failed to generate menu" }, { status: 500 })
}
}

27
app/api/orders/route.ts Normal file
View File

@@ -0,0 +1,27 @@
import { type NextRequest, NextResponse } from "next/server"
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const userId = searchParams.get("userId")
if (!userId) {
return NextResponse.json({ code: 400, message: "缺少用户ID" }, { status: 400 })
}
// In production, fetch from database
// For now, return mock data
const orders = []
console.log("[v0] Fetching orders for user:", userId)
return NextResponse.json({
code: 0,
message: "获取成功",
data: orders,
})
} catch (error) {
console.error("[v0] Get orders error:", error)
return NextResponse.json({ code: 500, message: "服务器错误" }, { status: 500 })
}
}

View File

@@ -0,0 +1,48 @@
import { type NextRequest, NextResponse } from "next/server"
import { AlipayService } from "@/lib/payment/alipay"
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const params: Record<string, string> = {}
formData.forEach((value, key) => {
params[key] = value.toString()
})
// 初始化支付宝服务
const alipay = new AlipayService({
appId: process.env.ALIPAY_APP_ID || "wx432c93e275548671",
partnerId: process.env.ALIPAY_PARTNER_ID || "2088511801157159",
key: process.env.ALIPAY_KEY || "lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp",
returnUrl: "",
notifyUrl: "",
})
// 验证签名
const isValid = alipay.verifySign(params)
if (!isValid) {
console.error("[v0] Alipay signature verification failed")
return new NextResponse("fail")
}
const { out_trade_no, trade_status, buyer_id, total_amount } = params
// 只处理支付成功的通知
if (trade_status === "TRADE_SUCCESS" || trade_status === "TRADE_FINISHED") {
console.log("[v0] Alipay payment success:", {
orderId: out_trade_no,
amount: total_amount,
buyerId: buyer_id,
})
// TODO: 更新订单状态、解锁内容、分配佣金
}
return new NextResponse("success")
} catch (error) {
console.error("[v0] Alipay notify error:", error)
return new NextResponse("fail")
}
}

View File

@@ -0,0 +1,46 @@
import { type NextRequest, NextResponse } from "next/server"
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { orderId, status, transactionId, amount, paymentMethod, signature } = body
console.log("[v0] Payment callback received:", {
orderId,
status,
transactionId,
amount,
paymentMethod,
})
// In production:
// 1. Verify signature from payment gateway
// 2. Update order status in database
// 3. Grant user access to content
// 4. Calculate and distribute referral commission
// Mock signature verification
// const isValid = verifySignature(body, signature)
// For now, accept all callbacks
const isValid = true
if (!isValid) {
return NextResponse.json({ code: 403, message: "签名验证失败" }, { status: 403 })
}
// Update order status
if (status === "success") {
// Grant access
console.log("[v0] Payment successful, granting access for order:", orderId)
}
return NextResponse.json({
code: 0,
message: "回调处理成功",
})
} catch (error) {
console.error("[v0] Payment callback error:", error)
return NextResponse.json({ code: 500, message: "服务器错误" }, { status: 500 })
}
}

View File

@@ -0,0 +1,88 @@
import { type NextRequest, NextResponse } from "next/server"
import { AlipayService } from "@/lib/payment/alipay"
import { WechatPayService } from "@/lib/payment/wechat"
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { userId, type, sectionId, sectionTitle, amount, paymentMethod, referralCode } = body
// Validate required fields
if (!userId || !type || !amount || !paymentMethod) {
return NextResponse.json({ code: 400, message: "缺少必要参数" }, { status: 400 })
}
// Generate order ID
const orderId = `ORDER_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
// Create order object
const order = {
orderId,
userId,
type, // "section" | "fullbook"
sectionId: type === "section" ? sectionId : undefined,
sectionTitle: type === "section" ? sectionTitle : undefined,
amount,
paymentMethod, // "wechat" | "alipay" | "usdt" | "paypal"
referralCode,
status: "pending", // pending | completed | failed | refunded
createdAt: new Date().toISOString(),
expireAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30 minutes
}
// According to the payment method, create a payment order
let paymentData = null
if (paymentMethod === "alipay") {
const alipay = new AlipayService({
appId: process.env.ALIPAY_APP_ID || "wx432c93e275548671",
partnerId: process.env.ALIPAY_PARTNER_ID || "2088511801157159",
key: process.env.ALIPAY_KEY || "lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp",
returnUrl: process.env.ALIPAY_RETURN_URL || `${process.env.NEXT_PUBLIC_BASE_URL}/payment/success`,
notifyUrl: process.env.ALIPAY_NOTIFY_URL || `${process.env.NEXT_PUBLIC_BASE_URL}/api/payment/alipay/notify`,
})
paymentData = alipay.createOrder({
outTradeNo: orderId,
subject: type === "section" ? `购买章节: ${sectionTitle}` : "购买整本书",
totalAmount: amount,
body: `知识付费-书籍购买`,
})
} else if (paymentMethod === "wechat") {
const wechat = new WechatPayService({
appId: process.env.WECHAT_APP_ID || "wx432c93e275548671",
appSecret: process.env.WECHAT_APP_SECRET || "25b7e7fdb7998e5107e242ebb6ddabd0",
mchId: process.env.WECHAT_MCH_ID || "1318592501",
apiKey: process.env.WECHAT_API_KEY || "wx3e31b068be59ddc131b068be59ddc2",
notifyUrl: process.env.WECHAT_NOTIFY_URL || `${process.env.NEXT_PUBLIC_BASE_URL}/api/payment/wechat/notify`,
})
const clientIp = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "127.0.0.1"
paymentData = await wechat.createOrder({
outTradeNo: orderId,
body: type === "section" ? `购买章节: ${sectionTitle}` : "购买整本书",
totalFee: amount,
spbillCreateIp: clientIp.split(",")[0].trim(),
})
}
return NextResponse.json({
code: 0,
message: "订单创建成功",
data: {
...order,
paymentData,
},
})
} catch (error) {
console.error("[v0] Create order error:", error)
return NextResponse.json(
{
code: 500,
message: error instanceof Error ? error.message : "服务器错误",
},
{ status: 500 },
)
}
}

View File

@@ -0,0 +1,46 @@
import { type NextRequest, NextResponse } from "next/server"
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { orderId, paymentMethod, transactionId } = body
if (!orderId) {
return NextResponse.json({ code: 400, message: "缺少订单号" }, { status: 400 })
}
// In production, verify with payment gateway API
// For now, simulate verification
console.log("[v0] Verifying payment:", { orderId, paymentMethod, transactionId })
// Simulate verification delay
await new Promise((resolve) => setTimeout(resolve, 500))
// Mock verification result (95% success rate)
const isVerified = Math.random() > 0.05
if (isVerified) {
return NextResponse.json({
code: 0,
message: "支付验证成功",
data: {
orderId,
status: "completed",
verifiedAt: new Date().toISOString(),
},
})
} else {
return NextResponse.json({
code: 1,
message: "支付未完成,请稍后再试",
data: {
orderId,
status: "pending",
},
})
}
} catch (error) {
console.error("[v0] Verify payment error:", error)
return NextResponse.json({ code: 500, message: "服务器错误" }, { status: 500 })
}
}

View File

@@ -0,0 +1,59 @@
import { type NextRequest, NextResponse } from "next/server"
import { WechatPayService } from "@/lib/payment/wechat"
export async function POST(request: NextRequest) {
try {
const xmlData = await request.text()
const wechat = new WechatPayService({
appId: process.env.WECHAT_APP_ID || "wx432c93e275548671",
appSecret: process.env.WECHAT_APP_SECRET || "25b7e7fdb7998e5107e242ebb6ddabd0",
mchId: process.env.WECHAT_MCH_ID || "1318592501",
apiKey: process.env.WECHAT_API_KEY || "wx3e31b068be59ddc131b068be59ddc2",
notifyUrl: "",
})
// 解析XML数据
const params = await wechat["parseXML"](xmlData)
// 验证签名
const isValid = wechat.verifySign(params)
if (!isValid) {
console.error("[v0] WeChat signature verification failed")
return new NextResponse(
"<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>",
{
headers: { "Content-Type": "application/xml" },
},
)
}
const { out_trade_no, result_code, total_fee, openid } = params
if (result_code === "SUCCESS") {
console.log("[v0] WeChat payment success:", {
orderId: out_trade_no,
amount: Number.parseInt(total_fee) / 100,
openid,
})
// TODO: 更新订单状态、解锁内容、分配佣金
}
return new NextResponse(
"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>",
{
headers: { "Content-Type": "application/xml" },
},
)
} catch (error) {
console.error("[v0] WeChat notify error:", error)
return new NextResponse(
"<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[系统错误]]></return_msg></xml>",
{
headers: { "Content-Type": "application/xml" },
},
)
}
}