npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
-
项目框架 Next.js
-
Ui库 Flowbite
- 全局toast
components/ui/FlowbiteToasts
- 全局toast
-
后端服务 Supabase
- supabase 相关方法
utils/supabase
- 登录方法,项目目前使用邮箱otp和谷歌oauth
utils/auth-helpers
- 谷歌登录回调
auth/callback
- supabase 相关方法
-
支付服务 Stripe
- stripe 相关方法
utils/stripe
- stripe webhook
api/webhook
- stripe 产品数据获取并展示
components/PricingSection
- stripe 相关方法
-
博客 Contentlayer 和 MDX
- 博客内容
content/blog
- contentlayer 配置
contentlayer.config.js
- 博客内容
-
国际化i18n Next-intl
- 配置文件
i18n.ts | navigation.ts
- en无前缀Link组件
components/Link
- 配置文件
-
统计脚本
<AnalyticsScript/>
-
sitemap生成
sitempa.ts
-
metadata生成
- 简易生成使用
seo.tsx
- 定制生成
generateMetadata
- 简易生成使用
-
其他
- Header nav 文本和路径对应
NavBar 和 NavBarWithoutLocale 中的navLink
- 部分公共配置
config.js
- Header nav 文本和路径对应
https://dashboard.stripe.com/webhooks/create?endpoint_location=local https://docs.stripe.com/stripe-cli
stripe login
stripe listen --forward-to localhost:3000/api/webhook
- 选择随机一个付费方案,Premium或Basic
- 填入银行卡信息
- 银行卡号:4242 4242 4242 4242
- 年份随机 如:8/27
- cvc随机 如:344
- 名字和国家也随机填入
- 支付成功
- 账号积分增加
-- 用户表
create table users (
-- 用户id,取auth表中id
id uuid references auth.users not null primary key,
-- stripe顾客id
stripe_customer_id text,
-- 用户名
username text,
-- 用户头像
avatar_url text,
-- 用户邮箱
email text,
-- 当前总积分,默认10
plan_limit decimal(10,2) default 10.00,
-- 用户使用积分
plan_used decimal(10,2) default 0.00,
-- 用户今天使用积分数
today_used decimal(10,2) default 0.00,
-- 用户总共使用积分数
total_used decimal(10,2) default 0.00,
-- 套餐类型 0为普通用户,1为消费用户
plan_type integer default 0,
-- 套餐过期时间
plan_expire_at timestamp with time zone,
-- 用户总消费
paid_amount decimal(10,2) default 0.00,
-- 记录的创建时间
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
-- 记录的更新时间
updated_at timestamp with time zone default timezone('utc'::text, now()) not null
);
-- 订单状态类型 stripe order status
create type order_status_type as enum ('processing', 'canceled','succeeded','requires_action','requires_capture','requires_confirmation','requires_payment_method');
-- 订单表
create table orders (
-- 自增id
id serial primary key,
-- 用户id, 取auth表中id
user_id uuid references auth.users not null,
-- 账单id
trade_id text,
-- stripe顾客id
stripe_customer_id text,
-- 账单状态, processing, canceled, succeeded
status order_status_type,
-- 价格
amount bigint,
-- 货币
currency text,
-- 商品id,套餐id
product_id text,
-- 商品名
product_name text,
-- 商品描述
product_description text,
-- 支付时间,
paid_time timestamp with time zone,
-- 账单创建日期
date timestamp with time zone,
-- 记录的创建时间
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
-- 记录的更新时间
updated_at timestamp with time zone default timezone('utc'::text, now()) not null
);
/**
* trigger 新注册用户自动往users表插入一定数据
*/
-- trigger 已存在则删除
drop function if exists public.handle_new_user ();
create function public.handle_new_user()
returns trigger as
$$
declare
final_username text;
suffix_counter int := 1;
begin
-- 提取邮箱前缀并去除非字母数字字符
select regexp_replace(substring(new.email from '^[^@]+'), '[^a-z0-9]', '', 'g') into final_username;
-- 去除所有空格部分
final_username := replace(final_username, ' ', '');
-- 已存在username 处理为username_1_2_3
while exists (
select 1 from public.users where lower(username) = lower(final_username)
) loop
final_username := final_username || '_' || suffix_counter;
suffix_counter := suffix_counter + 1;
end loop;
insert into public.users (id, username, avatar_url, email)
values (
new.id,
final_username,
-- 如果没有提供avatar_url,使用'默认头像URL'
coalesce(new.raw_user_meta_data->>'avatar_url', ''),
new.email
);
return new;
end;
$$
language plpgsql security definer;
create trigger on_auth_user_created
after insert on auth.users
for each row
execute procedure public.handle_new_user();