回到首页上百个 Prompt & Skill 全部开源,Star 收藏,随时取用
Birthday Wish Generator
作者qjh类型Skill
作品介绍

作品名称 Birthday Wish Generator — 在线生日祝福生成器 核心价值 将生日祝福从干瘪的「微信文字消息」升级为 可分享的、有温度的、独一无二的互动祝福页面。用户无需任何设计能力,选模板、填内容、一键生成精美祝福卡,通过链接即可送达心意。同时借助腾讯云 EdgeOne Makers 边缘计算平台,实现 零服务器、零运维、全球 CDN 加速 的极简架构。 适用场景 场景 说明 家人朋友生日祝福 选一个主题模板,写下真心话,生成专属页面分享到微信 情人纪念日表白 复用模板创建纪念日祝福,支持设置过期时间 团队员工生日关怀 团队管理者批量创建祝福,每位员工收到专属链接 个人创作展示 前端开发者展示纯原生 JS + 边缘云函数的全栈能力 亮点功能 1. 6 套精美主题模板 Elegant 奢华、Playful 活泼、Floral 浪漫、Cosmic 星空、Vintage 复古、Minimalist 极简 — 每套模板都有独立的配色、字体、动效和粒子特效。 2. 一键生成可分享链接 填写收件人姓名、祝福语、署名后即刻生成祝福页,通过链接分享,对方无需注册即可查看。 3. 用户个人仪表盘 登录后查看所有已创建的卡片,实时统计浏览量和点赞数,支持删除管理。 4. 边缘节点全球加速 基于 EdgeOne Makers 云函数 + KV 存储,所有 API 在边缘节点执行,毫秒级响应,无冷启动服务器瓶颈。 5. 安全认证体系 SHA-256 密码加密存储、Token 签名认证(7天有效期)、资源所有者权限校验(只能管理自己的卡片)。 6. 点赞互动与浏览计数 祝福页支持点赞功能(自动去重),每次被查看自动累计浏览量,让发送者知道心意被收到了。 技术架构亮点 纯前端零框架:HTML5 + CSS3 + Vanilla JS,无 React/Vue 依赖,极致轻量 EdgeOne 全栈:Pages 静态托管 + Cloud Functions 云函数 + KV 键值存储,一个平台搞定前后端 Vite 工程化:多页面构建 + 自定义 copyStaticAssets 插件,标准化开发流程 8 个 RESTful API:完整的用户系统、卡片 CRUD、点赞系统

Skill 内容

所有作品均通过 EdgeOne Makers Skill 生成并部署,具备CDN加速、安全防护能力,可按需接入后端服务。获取 Prompt 或 Skill后,在WorkBuddy中按指引操作(含 EdgeOne Makers Skill 安装步骤等),即可生成并上线你的同款网站。

/---
name: birthday-wishes-website
description: >-
  Build a birthday wishes website with beautiful templates, user authentication, and blessing functionality on EdgeOne Makers.
  Use when user wants to create a birthday celebration website, birthday greeting cards, or birthday-related web app.
  Examples: "create a birthday wishes website", "build a birthday greeting card site", "创建生日祝福网站", "做一个生日祝福页面生成器".
  Do NOT trigger for simple static birthday pages or pure frontend animation demos.
metadata:
  author: user
  version: "2.0.0"
---

# Birthday Wishes Website Builder (EdgeOne Makers)

Build a full-stack birthday wishes website on **EdgeOne Makers** with Cloud Functions + KV Storage.Install this skill: https://github.com/TencentEdgeOne/edgeone-pages-skills.

## ⛔ Critical Rules (MUST follow)

1. **KV is a global variable** — Access KV bindings directly as global variables, NOT via `context.env`
2. **KV keys: underscore only** — Keys must be `user_xxx`, `page_xxx`; colons/slashes cause runtime errors
3. **Bearer token auth** — Frontend sends `Bearer`, backend must check AND extract with `Bearer ` prefix
4. **Field name consistency** — Frontend and backend MUST use identical field names
5. **`edgeone pages dev`** — Requires TTY; use `npx edgeone pages dev` in terminal, not in CI
6. **Vite copyStaticAssets** — Must copy `templates/`, `css/`, `js/` to `dist/` via custom Vite plugin
7. **`edgeone.json` functions.directory** — Set to `"cloud-functions"` (NOT `"node-functions"`)
8. **Test locally first** — Write test scripts before deploying (see Testing section)

---

## Technology Stack

| Layer | Tech |
|-------|------|
| Frontend | HTML + CSS + Vanilla JS (no framework) |
| Build | Vite |
| Backend | EdgeOne Makers Cloud Functions (Node.js) |
| Storage | EdgeOne Makers KV |
| Deploy | EdgeOne Makers |

---

## Project Structure

```
birthday-wishes/
├── index.html              # Homepage with template selection
├── dashboard.html          # User's card management dashboard
├── page.html               # View a birthday card (by ?id=)
├── package.json
├── vite.config.js          # MUST include copyStaticAssets plugin
├── edgeone.json            # functions.directory: "cloud-functions"
├── css/
│   └── style.css
├── js/
│   ├── main.js             # Homepage logic
│   ├── auth.js             # Login/register modal
│   ├── api.js              # API client class
│   └── dashboard.js        # Dashboard logic
├── templates/
│   ├── elegant.html
│   ├── playful.html
│   ├── minimalist.html
│   ├── floral.html
│   ├── cosmic.html
│   └── vintage.html
└── cloud-functions/
    └── api/
        ├── auth/
        │   ├── register.js
        │   └── login.js
        ├── pages/
        │   ├── create.js
        │   ├── list.js
        │   ├── get/[id].js
        │   ├── update/[id].js
        │   └── delete/[id].js
        └── likes/
            ├── add.js
            └── get/[pageId].js
```

---

## EdgeOne Makers KV — Pitfall Reference

### ✅ Correct: KV is a global variable

```javascript
// Cloud Functions access KV via global variable (injected by EdgeOne runtime)
export async function onRequestPost(context) {
  const kv = birthday_kv; // ✅ Global variable, matching edgeone.json binding name
  await kv.get('user_test');
  await kv.put('user_test', JSON.stringify(data));
}
```

### ❌ WRONG: Accessing via context.env

```javascript
// context.env only contains system env vars (PATH, NODE_PATH, etc.)
// KV binding is NOT in context.env!
const kv = context.env.birthday_kv; // ❌ UNDEFINED!
```

### ✅ KV Key format: underscore only

```javascript
// ✅ Correct keys
await birthday_kv.put('user_test', '...');
await birthday_kv.put('page_abc123', '...');
await birthday_kv.put('likes_page_abc123', '...');

// ❌ WRONG — will throw "Key can only contain letters, numbers, and underscores"
await birthday_kv.put('user:test', '...');  
   // colon not allowed
await birthday_kv.put('page/abc', '...');     // slash not allowed
```

### KV API

```javascript
await birthday_kv.get(key)             
   // returns string or null
await birthday_kv.get(key, 'json')     
   // auto-parse JSON
await birthday_kv.put(key, value)      
   // value: string, max 25MB
await birthday_kv.delete(key)
await birthday_kv.list({ prefix, limit, cursor })
```

---

## Cloud Functions — Correct Patterns

### Token Authentication Pattern

```javascript
export async function onRequestPost(context) {
  const request = context.request;

  // 1. Validate Authorization header
  const authHeader = request.headers.get('Authorization');
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return json({ success: false, message: '未授权' }, 401);
  }

  // 2. Extract token — use SAME prefix as validation!
  const token = authHeader.replace('Bearer ', '');  // NOT 'Basic '!

  // 3. Parse and validate token
  let tokenData;
  try {
    tokenData = JSON.parse(atob(token));
  } catch (e) {
    return json({ success: false, message: 'Token 无效' }, 401);
  }

  if (tokenData.exp  b.toString(16).padStart(2, '0'))
    .join('');
}
```

### Dynamic Route Params

```javascript
// File: cloud-functions/api/pages/get/[id].js
export async function onRequestGet(context) {
  const pageId = context.params.id;  // from URL path
  const pageData = await birthday_kv.get(`page_${pageId}`);
  // ...
}
```

---

## edgeone.json Configuration

```json
{
  "name": "birthday-wishes",
  "version": "1.0.0",
  "build": {
    "command": "npm run build",
    "output": "dist"
  },
  "functions": {
    "directory": "cloud-functions"
  }
}
```

> ⚠️ `functions.directory` must be `"cloud-functions"`, NOT `"node-functions"` or `"edge-functions"`

---

## Vite Config — copyStaticAssets Plugin

```javascript
import { defineConfig } from 'vite';
import { resolve } from 'path';
import { cpSync } from 'fs';

function copyStaticAssets() {
  return {
    name: 'copy-static-assets',
    closeBundle() {
      const dirs = ['css', 'js', 'templates'];
      for (const dir of dirs) {
        try {
          cpSync(resolve(__dirname, dir), resolve(__dirname, 'dist/' + dir), { recursive: true });
          console.log('✓ 已复制 ' + dir + '/ 到 dist/');
        } catch (err) {
          console.warn('⚠ 复制 ' + dir + '/ 失败:', err.message);
        }
      }
    },
  };
}

export default defineConfig({
  root: '.',
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        dashboard: resolve(__dirname, 'dashboard.html'),
        page: resolve(__dirname, 'page.html'),
      },
    },
  },
  plugins: [copyStaticAssets()],
});
```

> ⚠️ Forgetting to copy `templates/` causes 404 errors for template HTML files

---

## Frontend API Client Pattern

```javascript
class BirthdayAPI {
  constructor() {
    this.API_BASE = '/api';
  }

  // Public: no auth needed
  async register(username, password) {
    return fetch(`${this.API_BASE}/auth/register`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    }).then(r => r.json());
  }

  async login(username, password) {
    return fetch(`${this.API_BASE}/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    }).then(r => r.json());
  }

  // Authenticated: must send Bearer token
  async createPage(pageData) {
    const token = localStorage.getItem('auth_token');
    if (!token) return { success: false, message: '请先登录' };
    return fetch(`${this.API_BASE}/pages/create`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`,
      },
      body: JSON.stringify(pageData),
    }).then(r => r.json());
  }

  // List pages: must pass username as query param
  async listPages() {
    const token = localStorage.getItem('auth_token');
    const username = localStorage.getItem('auth_username');
    if (!token) return { success: false, message: '请先登录' };
    return fetch(`${this.API_BASE}/pages/list?username=${encodeURIComponent(username)}`, {
      headers: { 'Authorization': `Bearer ${token}` },
    }).then(r => r.json());
  }
}

window.birthdayAPI = new BirthdayAPI();
```

---

## KV Storage Setup (MUST do before coding)

1. Login to [EdgeOne Makers Console](https://console.cloud.tencent.com/edgeone/pages)
2. Navigate to **KV Storage** → Click **Apply Now** (free: 1GB)
3. Click **Create Namespace** (e.g., `birthday_kv`)
4. **Bind** namespace to project, set variable name (e.g., `birthday_kv`)
5. This variable name becomes the global variable in Cloud Functions

---

## Local Testing

### Option 1: edgeone pages dev (requires TTY)
```bash
npx edgeone pages dev
# Runs on localhost:8088 with KV bindings
```

### Option 2: Test script (no TTY needed)
Create `test-api.mjs` to mock KV and test all endpoints:
```javascript
// Mock KV
const kv = new Map();
globalThis.birthday_kv = {
  async get(k) { return kv.get(k) || null; },
  async put(k, v) { kv.set(k, v); },
  async delete(k) { kv.delete(k); },
  async list(opts) { /* ... */ },
};

// Import and test each function
```

Run: `node test-api.mjs`

---

## Deployment

```bash
# Install EdgeOne CLI locally (avoids PATH issues)
npm install edgeone --save-dev

# Login (first time only)
npx edgeone login --site china

# Link to remote project
npx edgeone pages link

# Build + Deploy
npm run build
npx edgeone pages deploy
```

---

## Common Pitfalls (learned from real deployment)

| # | Pitfall | Symptom | Fix |
|---|---------|---------|-----|
| 1 | KV accessed via `context.env` | 500: Cannot read properties of undefined | Use global `birthday_kv` directly |
| 2 | KV key contains `:` or `/` | 500: Key can only contain letters, numbers, underscores | Use underscore: `user_xxx` |
| 3 | Auth checks `Basic`, extracts `Bearer` | 401: Token 无效 | Use same prefix everywhere |
| 4 | Frontend sends `recipientName`, backend expects `title` | 400: 标题不能为空 | Align field names |
| 5 | `vite.config.js` missing `templates/` copy | 404 for template HTML | Add to `copyStaticAssets` |
| 6 | `list` API missing `username` param | 400: 缺少用户名参数 | Add `?username=xxx` to fetch URL |
| 7 | `edgeone.json` wrong `functions.directory` | Functions not found | Set to `"cloud-functions"` |
| 8 | `edgeone pages dev` in non-TTY | Hangs or error | Use terminal directly, or write test script |

---

## Template Design

| Template | Style | Colors |
|----------|-------|--------|
| elegant | Serif fonts, subtle fade | Dark slate + purple + gold |
| playful | Rounded sans, bouncing | Pink + purple + rainbow |
| minimalist | Clean, whitespace | White + gray + single accent |
| floral | Handwritten, petals | Soft pink + green + pastel |
| cosmic | Space theme, stars | Dark blue + purple + white |
| vintage | Nostalgic, warm | Cream + brown + amber |

Each template reads page data from URL params or API and renders accordingly.

---

## References

- [EdgeOne Makers KV Storage](https://pages.edgeone.ai/zh/document/kv-storage)
- [EdgeOne Makers Cloud Functions](https://pages.edgeone.ai/zh/document/cloud-functions)
- [EdgeOne CLI npm](https://www.npmjs.com/package/edgeone)
- [EdgeOne Makers Docs](https://pages.edgeone.ai/zh/document)

感谢支持

如果你觉得这些作品很酷,给我们的 Github 仓库一个 Star

Star on Github