Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,43 @@ pnpm install
stripe login
```

去vercel上面申请了neon-green-chair免费版的数据库,并尝试建立本地连接,方便下一步部署

Use the included setup script to create your `.env` file:

```bash
pnpm db:setup
```

```bash
pnpm db:setup

> @ db:setup C:\Users\FX\IdeaProjects\fx-saas
> npx tsx lib/db/setup.ts

Step 1: Checking if Stripe CLI is installed and authenticated...
Stripe CLI is installed.
Stripe CLI is authenticated.
Step 2: Setting up Postgres
Do you want to use a local Postgres instance with Docker (L) or a remote Postgres instance (R)? (L/R): R 选择R远程数据库
You can find Postgres databases at: https://vercel.com/marketplace?category=databases
Enter your POSTGRES_URL: 去vercel上面申请了neon-green-chair免费版的数据库,并尝试建立本地连接
Step 3: Getting Stripe Secret Key
You can find your Stripe Secret Key at: https://dashboard.stripe.com/test/apikeys
Enter your Stripe Secret Key: 去该网站申请测试KEY https://dashboard.stripe.com/acct_1SIrbsICXUfDaKJj/test/apikeys
Step 4: Creating Stripe webhook...
Stripe webhook created.
Step 5: Generating AUTH_SECRET...
Stripe webhook created.
Step 5: Generating AUTH_SECRET...
Step 6: Writing environment variables to .env
.env file created with the necessary variables.
🎉 Setup completed successfully!
Step 6: Writing environment variables to .env
.env file created with the necessary variables.
🎉 Setup completed successfully!
🎉 Setup completed successfully!
```
Run the database migrations and seed the database with a default user and team:

```bash
Expand Down Expand Up @@ -74,6 +105,28 @@ You can listen for Stripe webhooks locally through their CLI to handle subscript
stripe listen --forward-to localhost:3000/api/stripe/webhook
```

登录 Stripe Dashboard
👉 https://dashboard.stripe.com/test/webhooks

点击「+ Add endpoint」

输入你的 Webhook 地址,例如:

https://fx-saas.vercel.app/api/stripe/webhook


选择要监听的事件(例如 checkout.session.completed, invoice.paid 等)
创建成功后,页面会显示一个类似:

whsec_live_xxx


的 Signing secret

将它填入线上环境变量(Vercel 的 Dashboard → Project → Settings → Environment Variables):

STRIPE_WEBHOOK_SECRET=whsec_live_xxx

## Testing Payments

To test Stripe payments, use the following test card details:
Expand All @@ -98,6 +151,8 @@ When you're ready to deploy your SaaS application to production, follow these st
2. Connect your repository to [Vercel](https://vercel.com/) and deploy it.
3. Follow the Vercel deployment process, which will guide you through setting up your project.

进入 Vercel 项目的 Settings → Environment Variables建立环境变量,导入本地env文件即可

### Add environment variables

In your Vercel project settings (or during deployment), add all the necessary environment variables. Make sure to update the values for the production environment, including:
Expand Down
2 changes: 1 addition & 1 deletion app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function Header() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
<Link href="/" className="flex items-center">
<CircleIcon className="h-6 w-6 text-orange-500" />
<span className="ml-2 text-xl font-semibold text-gray-900">ACME</span>
<span className="ml-2 text-xl font-semibold text-gray-900">FanXiao</span>
</Link>
<div className="flex items-center space-x-4">
<Suspense fallback={<div className="h-9" />}>
Expand Down
59 changes: 35 additions & 24 deletions app/(dashboard)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Button } from '@/components/ui/button';
import { ArrowRight, CreditCard, Database } from 'lucide-react';
import {
BookOpen, // 📖 书籍 - 诗歌/文学
Clapperboard, // 🎬 电影板 - 导演梦想
Heart, // 💖 爱心 - 浪漫主义
ArrowRight
} from 'lucide-react';
import { Terminal } from './terminal';

export default function HomePage() {
Expand All @@ -10,13 +15,11 @@ export default function HomePage() {
<div className="lg:grid lg:grid-cols-12 lg:gap-8">
<div className="sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left">
<h1 className="text-4xl font-bold text-gray-900 tracking-tight sm:text-5xl md:text-6xl">
Build Your SaaS
<span className="block text-orange-500">Faster Than Ever</span>
欢迎来到我的网站
<span className="block text-orange-500">你今天开心吗?</span>
</h1>
<p className="mt-3 text-base text-gray-500 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
Launch your SaaS product in record time with our powerful,
ready-to-use template. Packed with modern technologies and
essential integrations.
把你不开心的事情说出来,让大家开心一下
</p>
<div className="mt-8 sm:max-w-lg sm:mx-auto sm:text-center lg:text-left lg:mx-0">
<a
Expand All @@ -28,7 +31,7 @@ export default function HomePage() {
variant="outline"
className="text-lg rounded-full"
>
Deploy your own
这是一个跳转连接
<ArrowRight className="ml-2 h-5 w-5" />
</Button>
</a>
Expand All @@ -46,46 +49,56 @@ export default function HomePage() {
<div className="lg:grid lg:grid-cols-3 lg:gap-8">
<div>
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-orange-500 text-white">
<svg viewBox="0 0 24 24" className="h-6 w-6">
{/* <svg viewBox="0 0 24 24" className="h-6 w-6">
<path
fill="currentColor"
d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
/>
</svg>
</svg> */}
<Heart className="h-6 w-6" />
</div>
<div className="mt-5">
<h2 className="text-lg font-medium text-gray-900">
Next.js and React
樊宵
</h2>
<p className="mt-2 text-base text-gray-500">
Leverage the power of modern web technologies for optimal
performance and developer experience.
时代浪漫主义诗人 • 作家 • 学者
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div className="text-lg">
<span className="text-amber-300">⚽</span> 足球巨星不是梦
</div>
<div className="text-lg">
<span className="text-emerald-300">🎬</span> 未来导演是行动
</div>
<div className="text-lg">
<span className="text-rose-300">✍️</span> 浪漫主义实践者
</div>
</div>
</div>
</div>

<div className="mt-10 lg:mt-0">
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-orange-500 text-white">
<Database className="h-6 w-6" />
<Clapperboard className="h-6 w-6" />
</div>
<div className="mt-5">
<h2 className="text-lg font-medium text-gray-900">
Postgres and Drizzle ORM
探索我的世界
</h2>
<p className="mt-2 text-base text-gray-500">
Robust database solution with an intuitive ORM for efficient
data management and scalability.
在时代浪潮中,追寻诗意的足球,在光影之间,书写浪漫的导演梦
</p>
</div>
</div>

<div className="mt-10 lg:mt-0">
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-orange-500 text-white">
<CreditCard className="h-6 w-6" />
<BookOpen className="h-6 w-6" />
</div>
<div className="mt-5">
<h2 className="text-lg font-medium text-gray-900">
Stripe Integration
阅读诗集
</h2>
<p className="mt-2 text-base text-gray-500">
Seamless payment processing and subscription management with
Expand All @@ -102,12 +115,10 @@ export default function HomePage() {
<div className="lg:grid lg:grid-cols-2 lg:gap-8 lg:items-center">
<div>
<h2 className="text-3xl font-bold text-gray-900 sm:text-4xl">
Ready to launch your SaaS?
诗是流动的光影,梦是无尽的绿茵。
</h2>
<p className="mt-3 max-w-3xl text-lg text-gray-500">
Our template provides everything you need to get your SaaS up
and running quickly. Don't waste time on boilerplate - focus on
what makes your product unique.
这里是我的个人诗歌集与梦想日志——时代浪漫主义的片段与隽语,亦或足球场上的奔跑和电影镜头下的故事。
</p>
</div>
<div className="mt-8 lg:mt-0 flex justify-center lg:justify-end">
Expand All @@ -117,7 +128,7 @@ export default function HomePage() {
variant="outline"
className="text-lg rounded-full"
>
View the code
联系我
<ArrowRight className="ml-3 h-6 w-6" />
</Button>
</a>
Expand Down
15 changes: 8 additions & 7 deletions app/(dashboard)/terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ export function Terminal() {
const [terminalStep, setTerminalStep] = useState(0);
const [copied, setCopied] = useState(false);
const terminalSteps = [
'git clone https://github.com/nextjs/saas-starter',
'pnpm install',
'pnpm db:setup',
'pnpm db:migrate',
'pnpm db:seed',
'pnpm dev 🎉',
'樊宵 · 时代诗人',
'逐梦足球场',
'光影导演人生',
'浪漫主义的自由书写',
'"诗,是流动的光影与不灭的梦想"',
'—— 你好,世界 🎉',
];


useEffect(() => {
const timer = setTimeout(() => {
setTerminalStep((prev) =>
prev < terminalSteps.length - 1 ? prev + 1 : prev
);
}, 500);
}, 1000);

return () => clearTimeout(timer);
}, [terminalStep]);
Expand Down
16 changes: 8 additions & 8 deletions app/(login)/login.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
'use client';
'use client';// 声明这是前端组件,支持事件和交互

import Link from 'next/link';
import { useActionState } from 'react';
import { useSearchParams } from 'next/navigation';
import { Button } from '@/components/ui/button';
import Link from 'next/link';// 跳转路由
import { useActionState } from 'react';// React 18 的表单处理新钩子
import { useSearchParams } from 'next/navigation';// 读取URL参数,支持跳转、邀请等
import { Button } from '@/components/ui/button';// 引入项目自定义UI组件
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { CircleIcon, Loader2 } from 'lucide-react';
import { signIn, signUp } from './actions';
import { ActionState } from '@/lib/auth/middleware';
import { CircleIcon, Loader2 } from 'lucide-react';// 图标,用于美化UI
import { signIn, signUp } from './actions';// 导入实际登录/注册逻辑(应该是 server actions)
import { ActionState } from '@/lib/auth/middleware';// 状态类型定义

export function Login({ mode = 'signin' }: { mode?: 'signin' | 'signup' }) {
const searchParams = useSearchParams();
Expand Down
29 changes: 24 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
// 引入全局样式表,作用于整个 APP
import './globals.css';
// 导入 Next.js 中 Metadata(页面元信息)、Viewport 类型(用于响应式)
import type { Metadata, Viewport } from 'next';
import { Manrope } from 'next/font/google';
// 导入 Google Fonts 字体加载器(Manrope 字体)
import { Noto_Sans_SC, Noto_Sans_JP, Manrope } from 'next/font/google';
// 导入用户/团队获取工具函数(通常用于数据预加载或 fallback 数据)
import { getUser, getTeamForUser } from '@/lib/db/queries';
// 导入 SWR 的全局 Provider,用于统一缓存和数据请求配置
import { SWRConfig } from 'swr';

// 配置页面的基础元数据(SEO 标题、简介、分享描述等)
export const metadata: Metadata = {
title: 'Next.js SaaS Starter',
description: 'Get started quickly with Next.js, Postgres, and Stripe.'
title: 'FX',
description: '樊宵(Fan Xiao),专注于AI与程序设计融合的创新开发,打造中日跨境B2B服务与智能化系统,让技术与创造力共同驱动未来。'
};

// 配置移动端 viewport 行为,这里禁止页面最大缩放超过 1(防止用户手动放大页面)
export const viewport: Viewport = {
maximumScale: 1
};

const manrope = Manrope({ subsets: ['latin'] });
// 加载 Manrope Web 字体,仅拉取 'latin'(英文字母)子集以加速加载
const manrope = Manrope({ subsets: ['latin'] , display: 'swap' });
const jp = Noto_Sans_JP({ display: 'swap' });
const sc = Noto_Sans_SC({ display: 'swap' });

/**
* RootLayout 是所有页面外层的全局布局组件
* @param children 所有实际页面内容会被当做 children 进行渲染
*/
export default function RootLayout({
children
}: {
children: React.ReactNode;
}) {
return (
// 设置网页语言为英语 html 元素,并加入自定义字体样式
// 网站主体 body 设置亮/暗色主题自动切换
// SWRConfig 配置全局数据缓存/fallback(首屏异步数据预热),所有子组件都能用
<html
lang="en"
lang="en"
className={`bg-white dark:bg-gray-950 text-black dark:text-white ${manrope.className}`}
>
<body className="min-h-[100dvh] bg-gray-50">
Expand All @@ -31,11 +48,13 @@ export default function RootLayout({
fallback: {
// We do NOT await here
// Only components that read this data will suspend
// 这里不 await,getUser() 和 getTeamForUser() 返回的是 Promise,只有真正用到才开始 suspend 等待
'/api/user': getUser(),
'/api/team': getTeamForUser()
}
}}
>
{/* 渲染页面实际内容(即各路由对应的页面组件) */}
{children}
</SWRConfig>
</body>
Expand Down
10 changes: 10 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
// 导入 Next.js 的 Link 组件,用于跳转到首页或其他正确路由
import Link from 'next/link';
// 导入 lucide-react 图标库的 CircleIcon,作为页面视觉提示
import { CircleIcon } from 'lucide-react';

/**
* NotFound 组件是 Next.js app 路由下自动识别的 404 页面
* 任何不存在的地址都会自动渲染这里的内容
*/
export default function NotFound() {
return (
<div className="flex items-center justify-center min-h-[100dvh]">
<div className="max-w-md space-y-8 p-4 text-center">
<div className="flex justify-center">
{/* 圆形图标,增加错误提示的视觉效果 */}
<CircleIcon className="size-12 text-orange-500" />
</div>
{/* 主要错误标题,清晰告知用户页面未找到 */}
<h1 className="text-4xl font-bold text-gray-900 tracking-tight">
Page Not Found
</h1>
{/* 错误原因解释,帮助用户理解可能发生的问题 */}
<p className="text-base text-gray-500">
The page you are looking for might have been removed, had its name
changed, or is temporarily unavailable.
</p>
{/* 提供返回首页链接,帮助用户自救继续访问 */}
<Link
href="/"
className="max-w-48 mx-auto flex justify-center py-2 px-4 border border-gray-300 rounded-full shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500"
Expand Down
24 changes: 24 additions & 0 deletions task.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
20251017:
本地部署成功
vercel 部署成功
vercel申请了pgsqlDB成功
申请了stripe账户和密钥
理解了stripe支付的webhook,并与本地连接成功
npm run db:generate -- lib/db/schema.ts
npm run db:migrate -- lib/db/migrations/sql脚本执行
以上两个命令的使用场景是新增表-新业务情况
----------------------------------------------
Let's Encrypt 是免费的证书颁发机构(CA),可以帮你申请 免费 SSL 证书。
配合 Nginx,可以自动配置 HTTPS,浏览器和 Stripe 都能正常访问。
命令解释:
yum install -y certbot python3-certbot-nginx # 安装 Certbot 工具和 Nginx 插件
certbot --nginx -d your-domain.com # 生成证书并自动修改 Nginx 配置
----------------------------------------------




下一个阶段是尝试部署到serv00服务器或其他服务器
尝试开发几个页面
尝试开发几个接口
尝试直接访问接口