mcpField
F
firebase-app-platform

firebase-app-platform

github.com/bagelhole开发1.2k314 次安装23 周前更新
代码仓库

关于 firebase-app-platform

这项技能指南介绍如何使用 Firebase 托管服务构建移动和 Web 后端,适合需要身份验证、实时数据、无服务器 API 或网站托管的开发者,并涵盖配置与运维示例。

主要功能

  1. 01Firestore 数据访问与安全规则
  2. 02Firebase Authentication 配置
  3. 03使用 Cloud Functions 构建无服务器 API
  4. 04网站与 SPA 的托管配置
  5. 05本地模拟器与多环境工作流

适用场景

  1. 01为应用添加实时数据同步
  2. 02以较少后端代码设置登录
  3. 03使用 Cloud Functions 构建无服务器端点
  4. 04通过 Firebase 模拟器进行本地测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill firebase-app-platform

安装后在 Claude Code 中输入/firebase-app-platform确认已加载。

详细介绍

Firebase App Platform

使用 Firebase 托管服务部署移动端和 Web 后端。

适用场景

在以下情况使用此技能:

  • 构建需要实时数据同步的移动端或 Web 应用
  • 需要身份验证,但希望尽量减少后端代码
  • 使用托管基础设施快速搭建原型
  • 使用 Cloud Functions 构建无服务器 API
  • 使用 CDN 托管静态网站或 SPA

前置条件

  • Node.js 18+
  • Firebase CLI(npm install -g firebase-tools)
  • Google Cloud 账号(Firebase 是 GCP 的一部分)
  • Firebase 项目(在 console.firebase.google.com 创建)

快速入门

# Install and authenticate
npm install -g firebase-tools
firebase login

# Initialize in your project directory
firebase init
# Select: Firestore, Functions, Hosting, Emulators

# Start local emulators
firebase emulators:start

# Deploy everything
firebase deploy

# Deploy specific services
firebase deploy --only functions
firebase deploy --only hosting
firebase deploy --only firestore:rules

Firestore 数据库

安全规则
// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Users can only read/write their own data
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }

    // Messages: authenticated users can read, only owner can write
    match /channels/{channelId}/messages/{messageId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null
        && request.resource.data.userId == request.auth.uid
        && request.resource.data.body is string
        && request.resource.data.body.size() <= 5000;
      allow update, delete: if request.auth != null
        && resource.data.userId == request.auth.uid;
    }

    // Admin-only collection
    match /admin/{document=**} {
      allow read, write: if request.auth != null
        && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin';
    }

    // Default: deny everything
    match /{document=**} {
      allow read, write: if false;
    }
  }
}
数据操作
// lib/firestore.ts
import { getFirestore, collection, doc, setDoc, getDoc,
         query, where, orderBy, limit, onSnapshot,
         serverTimestamp, increment } from "firebase/firestore";

const db = getFirestore();

// Create document with auto-ID
async function createMessage(channelId: string, body: string, userId: string) {
  const ref = doc(collection(db, "channels", channelId, "messages"));
  await setDoc(ref, {
    body,
    userId,
    createdAt: serverTimestamp(),
  });
  return ref.id;
}

// Real-time listener
function subscribeToMessages(channelId: string, callback: (msgs: any[]) => void) {
  const q = query(
    collection(db, "channels", channelId, "messages"),
    orderBy("createdAt", "desc"),
    limit(50)
  );
  return onSnapshot(q, (snapshot) => {
    const messages = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
    callback(messages);
  });
}

// Atomic counter
async function incrementViews(postId: string) {
  await setDoc(doc(db, "posts", postId), {
    views: increment(1),
  }, { merge: true });
}
索引
// firestore.indexes.json
{
  "indexes": [
    {
      "collectionGroup": "messages",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "channelId", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    }
  ]
}

身份验证

// lib/auth.ts
import { getAuth, signInWithPopup, GoogleAuthProvider,
         createUserWithEmailAndPassword, signInWithEmailAndPassword,
         signOut, onAuthStateChanged } from "firebase/auth";

const auth = getAuth();

// Google sign-in
async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();
  const result = await signInWithPopup(auth, provider);
  return result.user;
}

// Email/password registration
async function register(email: string, password: string) {
  const result = await createUserWithEmailAndPassword(auth, email, password);
  return result.user;
}

// Auth state listener
onAuthStateChanged(auth, (user) => {
  if (user) {
    console.log("Signed in:", user.uid, user.email);
  } else {
    console.log("Signed out");
  }
});

Cloud Functions

// functions/src/index.ts
import { onRequest } from "firebase-functions/v2/https";
import { onDocumentCreated } from "firebase-functions/v2/firestore";
import { getFirestore } from "firebase-admin/firestore";
import { initializeApp } from "firebase-admin/app";

initializeApp();
const db = getFirestore();

// HTTP function (API endpoint)
export const api = onRequest({ cors: true, region: "us-central1" }, async (req, res) => {
  if (req.method !== "GET") {
    res.status(405).send("Method not allowed");
    return;
  }
  const snapshot = await db.collection("posts").orderBy("createdAt", "desc").limit(10).get();
  const posts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
  res.json({ posts });
});

// Firestore trigger — runs when a new message is created
export const onMessageCreated = onDocumentCreated(
  "channels/{channelId}/messages/{messageId}",
  async (event) => {
    const data = event.data?.data();
    if (!data) return;

    // Update channel's last message timestamp
    await db.doc(`channels/${event.params.channelId}`).update({
      lastMessageAt: data.createdAt,
      messageCount: FieldValue.increment(1),
    });

    // Send notification (example)
    console.log(`New message in ${event.params.channelId}: ${data.body.substring(0, 50)}`);
  }
);

托管

// firebase.json
{
  "hosting": {
    "public": "dist",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      { "source": "/api/**", "function": "api" },
      { "source": "**", "destination": "/index.html" }
    ],
    "headers": [
      {
        "source": "**/*.@(js|css|svg|png|jpg|webp|woff2)",
        "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
      },
      {
        "source": "**",
        "headers": [
          { "key": "X-Frame-Options", "value": "DENY" },
          { "key": "X-Content-Type-Options", "value": "nosniff" },
          { "key": "Strict-Transport-Security", "value": "max-age=63072000" }
        ]
      }
    ]
  }
}

本地模拟器

# Start all emulators
firebase emulators:start

# Start specific emulators
firebase emulators:start --only auth,firestore,functions

# Export emulator data for persistence
firebase emulators:export ./emulator-data
firebase emulators:start --import=./emulator-data

# Emulator UI at http://localhost:4000
// firebase.json — emulator config
{
  "emulators": {
    "auth": { "port": 9099 },
    "firestore": { "port": 8080 },
    "functions": { "port": 5001 },
    "hosting": { "port": 5000 },
    "ui": { "enabled": true, "port": 4000 }
  }
}

环境配置

# Set environment variables for functions
firebase functions:config:set stripe.key="sk_live_xxx" app.name="MyApp"

# View config
firebase functions:config:get

# Use in functions (v1)
const stripeKey = functions.config().stripe.key;

# For v2 functions, use .env files
# functions/.env
STRIPE_KEY=sk_live_xxx

# functions/.env.local (for emulators)
STRIPE_KEY=sk_test_xxx

多环境设置

# Create separate projects for each environment
firebase use --add   # Add staging project alias
firebase use staging # Switch to staging
firebase use production

# Deploy to specific project
firebase deploy --project my-app-staging
firebase deploy --project my-app-production

# .firebaserc
{
  "projects": {
    "staging": "my-app-staging",
    "production": "my-app-production"
  }
}

CLI 参考

firebase projects:list              # List all projects
firebase deploy                      # Deploy everything
firebase deploy --only functions     # Deploy only functions
firebase deploy --only hosting       # Deploy only hosting
firebase deploy --only firestore     # Deploy rules + indexes
firebase functions:log               # View function logs
firebase hosting:channel:create pr-123  # Preview channel
firebase hosting:channel:delete pr-123

安全最佳实践

  • 在编写其他代码之前,先编写严格的 Firestore 安全规则
  • 按 Firebase 项目区分环境(staging/production)
  • 在 GCP 控制台启用预算提醒和配额监控
  • 将特权逻辑移至 Cloud Functions(绝不要信任客户端)
  • 使用 App Check 防止非应用客户端滥用 API
  • 启用 Firestore 审计日志以满足合规要求
  • 检查 OAuth 同意屏幕设置

故障排除

问题解决方案
权限被拒绝检查 Firestore 规则,确认身份验证状态
函数冷启动使用最小实例(minInstances: 1),优化导入
模拟器无法启动检查端口冲突,运行 firebase emulators:start --debug
部署失败运行 firebase deploy --debug,检查服务账号权限
规则测试失败使用 firebase emulators:exec 运行规则单元测试

相关技能

评论

支持 Markdown