一个现代化、可用于生产环境的 SaaS 模板,基于 SvelteKit 2、Svelte 5、Supabase 和 Skeleton UI 构建。让您的 SaaS 项目在几小时内启动并运行,而不是几个月。
# 克隆仓库
git clone https://github.com/yourusername/sveltey.git
cd sveltey
# 安装依赖
npm install
# 设置环境变量
cp .env.example .env
# 编辑 .env 文件,填入您的 Supabase 和 Stripe 密钥
# 启动开发服务器
npm run dev
访问 http://localhost:5173 并开始构建您的 SaaS!
src/
├── lib/
│ ├── components/ # 可复用的 UI 组件
│ ├── stores/ # Svelte 状态管理 stores
│ ├── utils/ # 工具函数
│ └── types/ # TypeScript 类型定义
├── routes/
│ ├── (app)/ # 应用路由
| | ├── api # API 路由,安全性在端点级别处理
| | ├── app # 应用路由,全局处理安全性,所有子路由均受保护
| | └── auth # 身份验证路由,用于登录、注销和密码重置功能
│ └── (marketing)/ # 公开营销页面
| ├── blog # 博客路由
| ├── contact # 联系我们页面
| ├── pricing # 定价页面
| ├── privacy # 隐私政策页面
| └── terms # 服务条款页面
├── app.html # 应用外壳
├── app.css # 全局样式
└── hooks.server.ts # 服务器钩子
在根目录创建一个 .env 文件:
# Supabase
PUBLIC_SUPABASE_URL=your_supabase_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
# Resend (用于电子邮件)
RESEND_API_KEY=your_resend_api_key
# Stripe // 即将推出
PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=your_webhook_secret
Sveltey 使用 Resend 提供可靠的电子邮件发送和卓越的开发者体验。
RESEND_API_KEY=re_your_api_key_here
// src/routes/api/send-email/+server.ts
import { RESEND_API_KEY } from '$env/static/private';
import { Resend } from 'resend';
const resend = new Resend(RESEND_API_KEY);
export async function POST({ request }) {
const { to, subject, html } = await request.json();
try {
const data = await resend.emails.send({
from: '[email protected]',
to,
subject,
html
});
return new Response(JSON.stringify({ success: true, data }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
在 src/lib/emails/ 中创建可复用的电子邮件模板:
// src/lib/emails/welcome.ts
export const welcomeEmail = (userName: string) => `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1 style="color: #333;">欢迎来到 Sveltey, ${userName}!</h1>
<p>感谢您加入我们的平台。我们很高兴您的加入。</p>
<a href="https://yourdomain.com/dashboard"
style="background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px;">
开始使用
</a>
</div>
`;
Sveltey 预配置了 Plausible Analytics - 一个轻量级、注重隐私的分析解决方案,默认符合 GDPR 规范。
分析脚本已集成在 src/app.html 中:
<script defer data-domain="sveltey.dev" src="https://events.plygrnd.org/js/script.js"></script>
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
data-domain="sveltey.dev" 替换为您实际的域名添加用户行为的自定义事件追踪:
// 追踪自定义事件
plausible('signup', { props: { plan: 'premium' } });
plausible('purchase', { props: { amount: 99 } });
要使用不同的数据分析提供商:
src/app.html 移除 Plausible 脚本app.html 以修改 favicon 和 meta 标签src/lib/config.ts 进行应用配置src/lib/components/ 目录下Sveltey 附带基于 svelte-meta-tags 构建的全面 SEO 系统,提供自动的 meta 标签管理、OpenGraph 支持和 Twitter Card 集成。
Sveltey 中的 Meta 标签系统使用两级方法:
src/routes/+layout.ts) - 整个站点的全局默认设置src/routes/*/+page.ts) - 页面特定的覆盖和添加基础 meta 标签定义在 src/routes/+layout.ts 中,包括:
const baseMetaTags = Object.freeze({
title: 'Sveltey - SvelteKit SaaS 模板',
titleTemplate: '%s | Sveltey',
description: '您的默认站点描述...',
canonical: new URL(url.pathname, url.origin).href,
robots: 'index,follow',
keywords: ['SvelteKit', 'SaaS', 'template', '模板'],
openGraph: {
type: 'website',
url: new URL(url.pathname, url.origin).href,
title: 'Sveltey - SvelteKit SaaS 模板',
description: '您的 OpenGraph 描述...',
siteName: 'Sveltey',
locale: 'zh_CN',
images: [
{
url: `${url.origin}/og-image.jpg`,
width: 1200,
height: 630,
alt: 'Sveltey - SvelteKit SaaS 模板',
type: 'image/jpeg'
}
]
},
twitter: {
cardType: 'summary_large_image',
site: '@sveltey_dev',
creator: '@sveltey_dev',
title: 'Sveltey - SvelteKit SaaS 模板',
description: '您的 Twitter 描述...',
image: `${url.origin}/og-image.jpg`,
imageAlt: 'Sveltey - SvelteKit SaaS 模板'
}
}) satisfies MetaTagsProps;
每个页面可以通过在其 +page.ts 文件中导出一个 load 函数来覆盖和扩展基础 meta 标签:
// src/routes/your-page/+page.ts
import type { MetaTagsProps } from 'svelte-meta-tags';
export const load = () => {
const pageMetaTags = Object.freeze({
title: '您的页面标题',
description: '此页面的具体描述',
keywords: ['此', '页面', '的', '额外', '关键字'],
openGraph: {
title: '您的页面标题 - 品牌名',
description: '用于社交分享的 OpenGraph 描述',
type: 'article', // 或 'website', 'product' 等
images: [
{
url: 'https://your-domain.com/specific-og-image.jpg',
width: 1200,
height: 630,
alt: '图片描述'
}
]
},
twitter: {
title: 'Twitter 特有的标题',
description: 'Twitter 特有的描述'
},
// 额外的 meta 标签
additionalMetaTags: [
{
name: 'author',
content: '您的名字'
},
{
property: 'article:published_time',
content: '2024-01-01T00:00:00Z'
}
]
}) satisfies MetaTagsProps;
return {
pageMetaTags
};
};
对于动态页面 (如博客文章),您可以根据内容生成 meta 标签:
// src/routes/blog/[slug]/+page.server.ts
export const load = async ({ params, url }) => {
const post = await getPostBySlug(params.slug);
const pageMetaTags = Object.freeze({
title: post.title,
description: post.excerpt,
canonical: new URL(`/blog/${params.slug}`, url.origin).href,
openGraph: {
type: 'article',
title: post.title,
description: post.excerpt,
url: new URL(`/blog/${params.slug}`, url.origin).href,
images: post.featuredImage
? [
{
url: post.featuredImage,
width: 1200,
height: 630,
alt: post.title
}
]
: undefined,
article: {
publishedTime: post.publishedAt,
authors: [post.author],
section: '技术',
tags: post.tags
}
}
}) satisfies MetaTagsProps;
return { post, pageMetaTags };
};
控制搜索引擎对每个页面的索引:
const pageMetaTags = {
robots: 'noindex,nofollow', // 不索引此页面
// 或
robots: 'index,follow', // 索引此页面 (默认)
// 或
robots: 'index,nofollow' // 索引但不跟踪链接
};
OpenGraph 图片对于社交媒体分享和 SEO 至关重要。Sveltey 提供了一个灵活的系统来管理这些图片。
将默认的 OpenGraph 图片放置在 static 文件夹中:
static/
├── og-image.jpg # 默认 1200x630 图片
├── og-image-square.jpg # 可选的方形变体
└── favicon.png
默认图片会在您的基础 meta 标签中被自动引用:
// src/routes/+layout.ts
openGraph: {
images: [
{
url: `${url.origin}/og-image.jpg`,
width: 1200,
height: 630,
alt: 'Sveltey - SvelteKit SaaS 模板',
type: 'image/jpeg'
}
];
}
覆盖特定页面的默认图片:
// src/routes/pricing/+page.ts
const pageMetaTags = {
openGraph: {
images: [
{
url: `${url.origin}/og-pricing.jpg`,
width: 1200,
height: 630,
alt: 'Sveltey 定价计划',
type: 'image/jpeg'
}
]
}
};
对于博客文章或动态内容,您可以动态生成或指定图片:
// src/routes/blog/[slug]/+page.server.ts
const pageMetaTags = {
openGraph: {
images: post.featuredImage
? [
{
url: post.featuredImage,
width: 1200,
height: 630,
alt: post.title,
type: 'image/jpeg'
}
]
: [
{
url: `${url.origin}/og-blog-default.jpg`,
width: 1200,
height: 630,
alt: 'Sveltey 博客',
type: 'image/jpeg'
}
]
}
};
// 包含多个图片变体的示例
openGraph: {
images: [
{
url: `${url.origin}/og-image-large.jpg`,
width: 1200,
height: 630,
alt: '用于 Facebook、LinkedIn 的大图',
type: 'image/jpeg'
},
{
url: `${url.origin}/og-image-square.jpg`,
width: 1080,
height: 1080,
alt: '用于 Instagram、Twitter 的方图',
type: 'image/jpeg'
}
];
}
对于高级用例,您可以动态生成图片:
// src/routes/api/og/[slug]/+server.ts
export async function GET({ params, url }) {
const post = await getPostBySlug(params.slug);
// 使用像 @vercel/og 或 canvas 这样的库生成图片
const image = await generateOGImage({
title: post.title,
author: post.author,
template: 'blog-post'
});
return new Response(image, {
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=31536000, immutable'
}
});
}
然后在您的 meta 标签中引用它:
openGraph: {
images: [
{
url: `${url.origin}/api/og/${params.slug}`,
width: 1200,
height: 630,
alt: post.title,
type: 'image/png'
}
];
}
使用这些工具测试您的 OpenGraph 实现:
openGraph: {
type: 'website', // website, article, product 等
title: '页面标题', // 社交分享的特定标题
description: '描述', // 社交媒体描述
url: 'https://example.com', // 规范 URL
siteName: '站点名称', // 您的站点/品牌名称
locale: 'zh_CN', // 语言和地区
// 针对文章
article: {
publishedTime: '2024-01-01T00:00:00Z',
modifiedTime: '2024-01-02T00:00:00Z',
authors: ['作者名'],
section: '技术',
tags: ['svelte', 'sveltekit']
},
// 针对产品
product: {
price: {
amount: '29.99',
currency: 'USD'
}
}
}
欢迎贡献!请随时提交 Pull Request。
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)本项目基于 MIT 许可证 - 有关详情,请参见 LICENSE 文件。
准备好发布您的 SaaS 了吗?今天就开始使用 Sveltey!