oasis-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOasis — Development Guide
Oasis — 开发指南
You are an expert on the Oasis platform. Use this knowledge when integrating, debugging, or reviewing Oasis code in Tauri applications.
您是Oasis平台的专家。在Tauri应用中集成、调试或审核Oasis代码时,请运用这些知识。
What Oasis Is
什么是Oasis
Oasis is a self-hosted platform for managing Tauri application releases, collecting user feedback, and tracking crashes. Developers integrate the SDK into their Tauri app, configure the release workflow, and Oasis handles update distribution, crash collection, and feedback management.
Repository: https://github.com/porkytheblack/oasis
License: MIT
Oasis是一个自托管平台,用于管理Tauri应用的发布、收集用户反馈和跟踪崩溃信息。开发者将SDK集成到Tauri应用中,配置发布工作流后,Oasis会负责更新分发、崩溃收集和反馈管理。
代码仓库:https://github.com/porkytheblack/oasis
许可证:MIT
Component Overview
组件概览
| Component | Purpose | Location |
|---|---|---|
| Client SDK: feedback, crashes, breadcrumbs | |
| Server | Hono backend API for releases, crashes, feedback | |
| Dashboard | Next.js admin UI for managing apps | |
| Release Workflow | Reusable GitHub Actions for Tauri builds | |
| 组件 | 用途 | 位置 |
|---|---|---|
| 客户端SDK:处理反馈、崩溃、面包屑 | |
| Server | 基于Hono的后端API,用于发布、崩溃、反馈管理 | |
| Dashboard | 基于Next.js的管理界面,用于管理应用 | |
| Release Workflow | 可复用的GitHub Actions,用于Tauri构建 | |
Architecture at a Glance
架构总览
Tauri App + SDK → Oasis Server → Dashboard
↓ ↓
Crash reports Release management
User feedback Update distribution
Breadcrumbs AnalyticsTauri App + SDK → Oasis Server → Dashboard
↓ ↓
Crash reports Release management
User feedback Update distribution
Breadcrumbs AnalyticsAPI Key Types
API密钥类型
| Type | Prefix | Use |
|---|---|---|
| Public | | SDK initialization (client-side) |
| CI | | GitHub Actions release registration |
| Admin | | Full dashboard access |
| 类型 | 前缀 | 用途 |
|---|---|---|
| 公开密钥 | | SDK初始化(客户端) |
| CI密钥 | | GitHub Actions发布注册 |
| 管理员密钥 | | 完整管理面板访问权限 |
Quick Start
快速开始
1. Install SDK
1. 安装SDK
bash
npm install oasis-sdkbash
npm install oasis-sdk2. Initialize
2. 初始化
typescript
import { initOasis } from 'oasis-sdk';
const oasis = initOasis({
apiKey: 'pk_my-app_xxxxxxxx', // From Oasis dashboard
serverUrl: 'https://updates.myapp.com', // Your Oasis server
appVersion: '1.0.0', // Current app version
enableAutoCrashReporting: true, // Auto-capture uncaught errors
});typescript
import { initOasis } from 'oasis-sdk';
const oasis = initOasis({
apiKey: 'pk_my-app_xxxxxxxx', // 来自Oasis管理面板
serverUrl: 'https://updates.myapp.com', // 你的Oasis服务器地址
appVersion: '1.0.0', // 当前应用版本
enableAutoCrashReporting: true, // 自动捕获未处理错误
});3. Collect Feedback
3. 收集用户反馈
typescript
await oasis.feedback.submit({
category: 'bug', // 'bug' | 'feature' | 'general'
message: 'Description of the issue',
email: 'user@example.com',
});
// Convenience methods
await oasis.feedback.reportBug('Bug description');
await oasis.feedback.requestFeature('Feature request');typescript
await oasis.feedback.submit({
category: 'bug', // 'bug' | 'feature' | 'general'
message: '问题描述',
email: 'user@example.com',
});
// 便捷方法
await oasis.feedback.reportBug('Bug描述');
await oasis.feedback.requestFeature('功能请求');4. Manual Crash Reporting
4. 手动上报崩溃
typescript
try {
riskyOperation();
} catch (error) {
await oasis.crashes.captureException(error, {
severity: 'error', // 'warning' | 'error' | 'fatal'
appState: { screen: 'checkout' },
});
}typescript
try {
riskyOperation();
} catch (error) {
await oasis.crashes.captureException(error, {
severity: 'error', // 'warning' | 'error' | 'fatal'
appState: { screen: 'checkout' },
});
}SDK API Reference
SDK API参考
OasisConfig
OasisConfig
typescript
interface OasisConfig {
apiKey: string; // Required: Public API key (pk_*)
serverUrl: string; // Required: Oasis server URL
appVersion: string; // Required: Current app version (semver)
enableAutoCrashReporting?: boolean; // Auto-capture errors (default: false)
maxBreadcrumbs?: number; // Max breadcrumbs (default: 50)
timeout?: number; // Request timeout ms (default: 10000)
debug?: boolean; // Enable debug logging (default: false)
beforeSend?: (event) => event | null; // Filter/modify events
onError?: (error, event) => void; // Called on send failure
}typescript
interface OasisConfig {
apiKey: string; // 必填:公开API密钥(pk_*)
serverUrl: string; // 必填:Oasis服务器地址
appVersion: string; // 必填:当前应用版本(语义化版本格式)
enableAutoCrashReporting?: boolean; // 自动捕获错误(默认:false)
maxBreadcrumbs?: number; // 最大面包屑数量(默认:50)
timeout?: number; // 请求超时时间(毫秒,默认:10000)
debug?: boolean; // 启用调试日志(默认:false)
beforeSend?: (event) => event | null; // 过滤/修改事件
onError?: (error, event) => void; // 发送失败时触发
}Feedback API
反馈API
typescript
await oasis.feedback.submit({ category, message, email?, metadata? });
await oasis.feedback.reportBug(message, email?);
await oasis.feedback.requestFeature(message, email?);
await oasis.feedback.sendFeedback(message, email?);typescript
await oasis.feedback.submit({ category, message, email?, metadata? });
await oasis.feedback.reportBug(message, email?);
await oasis.feedback.requestFeature(message, email?);
await oasis.feedback.sendFeedback(message, email?);Crash Reporting API
崩溃上报API
typescript
await oasis.crashes.captureException(error, { severity?, appState?, tags? });
await oasis.crashes.report({ error, severity?, appState?, tags? });
oasis.crashes.enableAutoCrashReporting();
oasis.crashes.disableAutoCrashReporting();typescript
await oasis.crashes.captureException(error, { severity?, appState?, tags? });
await oasis.crashes.report({ error, severity?, appState?, tags? });
oasis.crashes.enableAutoCrashReporting();
oasis.crashes.disableAutoCrashReporting();Breadcrumbs API
面包屑API
typescript
oasis.breadcrumbs.add({ type, message, data? });
oasis.breadcrumbs.addNavigation(from, to);
oasis.breadcrumbs.addClick(target, data?);
oasis.breadcrumbs.addHttp(method, url, statusCode?);
oasis.breadcrumbs.addConsole(level, message);
oasis.breadcrumbs.addUserAction(action, data?);
oasis.breadcrumbs.getAll();
oasis.breadcrumbs.clear();Auto-collected: Navigation, clicks, console messages, fetch requests.
typescript
oasis.breadcrumbs.add({ type, message, data? });
oasis.breadcrumbs.addNavigation(from, to);
oasis.breadcrumbs.addClick(target, data?);
oasis.breadcrumbs.addHttp(method, url, statusCode?);
oasis.breadcrumbs.addConsole(level, message);
oasis.breadcrumbs.addUserAction(action, data?);
oasis.breadcrumbs.getAll();
oasis.breadcrumbs.clear();自动收集内容:导航、点击、控制台消息、网络请求。
User Tracking
用户追踪
typescript
oasis.setUser({ id, email?, username?, ...custom });
oasis.setUser(null); // Clear usertypescript
oasis.setUser({ id, email?, username?, ...custom });
oasis.setUser(null); // 清除用户信息Utilities
工具方法
typescript
await oasis.flush(); // Flush event queue
oasis.getConfig(); // Get current config
oasis.destroy(); // Clean up resourcestypescript
await oasis.flush(); // 刷新事件队列
oasis.getConfig(); // 获取当前配置
oasis.destroy(); // 清理资源Tauri Configuration
Tauri配置
Enable Auto-Updates
启用自动更新
In :
src-tauri/tauri.conf.jsonjson
{
"tauri": {
"updater": {
"active": true,
"endpoints": [
"https://YOUR_OASIS_SERVER/your-app-slug/update/{{target}}/{{current_version}}"
],
"dialog": true,
"pubkey": "YOUR_PUBLIC_KEY_HERE"
}
}
}在中:
src-tauri/tauri.conf.jsonjson
{
"tauri": {
"updater": {
"active": true,
"endpoints": [
"https://YOUR_OASIS_SERVER/your-app-slug/update/{{target}}/{{current_version}}"
],
"dialog": true,
"pubkey": "YOUR_PUBLIC_KEY_HERE"
}
}
}Generate Signing Keys
生成签名密钥
bash
npx @tauri-apps/cli signer generate -w ~/.tauri/myapp.key- Private key → GitHub secret
TAURI_SIGNING_PRIVATE_KEY - Public key → under
tauri.conf.jsonupdater.pubkey
bash
npx @tauri-apps/cli signer generate -w ~/.tauri/myapp.key- 私钥 → 存入GitHub密钥
TAURI_SIGNING_PRIVATE_KEY - 公钥 → 填入的
tauri.conf.json字段updater.pubkey
GitHub Actions Workflow
GitHub Actions工作流
Create :
.github/workflows/release.ymlyaml
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
uses: porkytheblack/oasis/.github/workflows/tauri-release.yml@main
with:
app_slug: your-app-slug
artifact_prefix: YourApp
app_name: Your App Name
app_dir: .
distribute_to: r2,oasis,github
auto_publish: true
r2_public_url: https://cdn.example.com
secrets:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_R2_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
CLOUDFLARE_R2_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
OASIS_SERVER_URL: ${{ secrets.OASIS_SERVER_URL }}
OASIS_CI_KEY: ${{ secrets.OASIS_CI_KEY }}
NEXT_PUBLIC_OASIS_API_KEY: ${{ secrets.NEXT_PUBLIC_OASIS_API_KEY }}
NEXT_PUBLIC_OASIS_SERVER_URL: ${{ secrets.NEXT_PUBLIC_OASIS_SERVER_URL }}创建:
.github/workflows/release.ymlyaml
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
uses: porkytheblack/oasis/.github/workflows/tauri-release.yml@main
with:
app_slug: your-app-slug
artifact_prefix: YourApp
app_name: Your App Name
app_dir: .
distribute_to: r2,oasis,github
auto_publish: true
r2_public_url: https://cdn.example.com
secrets:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_R2_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
CLOUDFLARE_R2_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
OASIS_SERVER_URL: ${{ secrets.OASIS_SERVER_URL }}
OASIS_CI_KEY: ${{ secrets.OASIS_CI_KEY }}
NEXT_PUBLIC_OASIS_API_KEY: ${{ secrets.NEXT_PUBLIC_OASIS_API_KEY }}
NEXT_PUBLIC_OASIS_SERVER_URL: ${{ secrets.NEXT_PUBLIC_OASIS_SERVER_URL }}Required Secrets
必填密钥
| Secret | Description |
|---|---|
| Base64-encoded .p12 certificate |
| Certificate password |
| e.g., "Developer ID Application: Name (TEAMID)" |
| Apple ID email |
| App-specific password |
| Apple Developer Team ID |
| From |
| Cloudflare account ID |
| R2 API access key |
| R2 API secret key |
| R2 bucket name |
| Your Oasis server URL |
| CI API key ( |
| 密钥 | 说明 |
|---|---|
| Base64编码的.p12证书 |
| 证书密码 |
| 例如:"Developer ID Application: Name (TEAMID)" |
| Apple ID邮箱 |
| Apple专用密码 |
| Apple开发者团队ID |
| 来自 |
| Cloudflare账户ID |
| R2 API访问密钥 |
| R2 API密钥 |
| R2存储桶名称 |
| 你的Oasis服务器地址 |
| CI API密钥( |
Supported Platforms
支持的平台
| Platform | Target | Bundle Types |
|---|---|---|
| macOS (Apple Silicon) | | .dmg, .app.tar.gz |
| macOS (Intel) | | .dmg, .app.tar.gz |
| Linux | | .AppImage, .deb, .AppImage.tar.gz |
| Windows | | .exe (NSIS), .nsis.zip |
| 平台 | 目标架构 | 包类型 |
|---|---|---|
| macOS(Apple Silicon) | | .dmg, .app.tar.gz |
| macOS(Intel) | | .dmg, .app.tar.gz |
| Linux | | .AppImage, .deb, .AppImage.tar.gz |
| Windows | | .exe (NSIS), .nsis.zip |
Supporting Files
参考文档
For extended documentation including workflow details and integration checklist, see references/integration-guide.md.
如需了解工作流细节和集成清单等扩展文档,请查看references/integration-guide.md。
Common Gotchas
常见问题
- API key prefix matters: Public keys start with , CI keys with
pk_. Using the wrong type will fail silently.uk_live_ - Update endpoint URL format: Must include and
{{target}}placeholders exactly as shown.{{current_version}} - Signing key mismatch: The public key in must match the private key used in CI. Regenerating keys without updating both will break updates.
tauri.conf.json - Events not sending: Enable in SDK config to see network errors. Common causes: wrong serverUrl, CORS issues, invalid apiKey.
debug: true - Crashes not captured: Auto-capture only works if is set during
enableAutoCrashReporting: true, not after.initOasis() - macOS signing failures: must match the certificate name exactly, including the team ID in parentheses.
APPLE_SIGNING_IDENTITY - Release not appearing in app: Check in workflow. Unpublished releases exist in dashboard but aren't served to clients.
auto_publish: true - dry_run for testing: Use workflow input to test builds without uploading artifacts or registering releases.
dry_run: true - R2 public URL: The must be the public-facing CDN URL, not the R2 API endpoint.
r2_public_url - Version format: must be valid semver (e.g., "1.0.0", not "v1.0.0"). The workflow extracts version from git tags automatically.
appVersion
- API密钥前缀很重要:公开密钥以开头,CI密钥以
pk_开头。使用错误类型的密钥会静默失败。uk_live_ - 更新端点URL格式:必须严格包含和
{{target}}占位符。{{current_version}} - 签名密钥不匹配:中的公钥必须与CI中使用的私钥匹配。重新生成密钥后未同时更新两者会导致更新失败。
tauri.conf.json - 事件无法发送:在SDK配置中启用查看网络错误。常见原因:serverUrl错误、CORS问题、无效apiKey。
debug: true - 崩溃未被捕获:自动捕获仅在时设置
initOasis()才会生效,后续设置无效。enableAutoCrashReporting: true - macOS签名失败:必须与证书名称完全匹配,包括括号中的团队ID。
APPLE_SIGNING_IDENTITY - 发布未在应用中显示:检查工作流中的是否设置。未发布的版本仅存在于管理面板中,不会推送给客户端。
auto_publish: true - 测试用dry_run:使用工作流输入可测试构建,而无需上传制品或注册版本。
dry_run: true - R2公开URL:必须是公开可访问的CDN地址,而非R2 API端点。
r2_public_url - 版本格式:必须是有效的语义化版本(如"1.0.0",而非"v1.0.0")。工作流会自动从Git标签中提取版本。
appVersion