oasis-setup

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Oasis — 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.
Oasis是一个自托管平台,用于管理Tauri应用的发布、收集用户反馈和跟踪崩溃信息。开发者将SDK集成到Tauri应用中,配置发布工作流后,Oasis会负责更新分发、崩溃收集和反馈管理。
代码仓库https://github.com/porkytheblack/oasis 许可证:MIT

Component Overview

组件概览

ComponentPurposeLocation
oasis-sdk
Client SDK: feedback, crashes, breadcrumbs
npm install oasis-sdk
ServerHono backend API for releases, crashes, feedback
server/
DashboardNext.js admin UI for managing apps
dashboard/
Release WorkflowReusable GitHub Actions for Tauri builds
.github/workflows/tauri-release.yml
组件用途位置
oasis-sdk
客户端SDK:处理反馈、崩溃、面包屑
npm install oasis-sdk
Server基于Hono的后端API,用于发布、崩溃、反馈管理
server/
Dashboard基于Next.js的管理界面,用于管理应用
dashboard/
Release Workflow可复用的GitHub Actions,用于Tauri构建
.github/workflows/tauri-release.yml

Architecture at a Glance

架构总览

Tauri App + SDK → Oasis Server → Dashboard
       ↓                ↓
  Crash reports    Release management
  User feedback    Update distribution
  Breadcrumbs      Analytics
Tauri App + SDK → Oasis Server → Dashboard
       ↓                ↓
  Crash reports    Release management
  User feedback    Update distribution
  Breadcrumbs      Analytics

API Key Types

API密钥类型

TypePrefixUse
Public
pk_*
SDK initialization (client-side)
CI
uk_live_*
GitHub Actions release registration
Admin
uk_live_*
(admin scope)
Full dashboard access
类型前缀用途
公开密钥
pk_*
SDK初始化(客户端)
CI密钥
uk_live_*
GitHub Actions发布注册
管理员密钥
uk_live_*
(管理员权限)
完整管理面板访问权限

Quick Start

快速开始

1. Install SDK

1. 安装SDK

bash
npm install oasis-sdk
bash
npm install oasis-sdk

2. 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 user
typescript
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 resources
typescript
await oasis.flush();       // 刷新事件队列
oasis.getConfig();         // 获取当前配置
oasis.destroy();           // 清理资源

Tauri Configuration

Tauri配置

Enable Auto-Updates

启用自动更新

In
src-tauri/tauri.conf.json
:
json
{
  "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.json
中:
json
{
  "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
    tauri.conf.json
    under
    updater.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.yml
:
yaml
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.yml
yaml
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

必填密钥

SecretDescription
APPLE_CERTIFICATE
Base64-encoded .p12 certificate
APPLE_CERTIFICATE_PASSWORD
Certificate password
APPLE_SIGNING_IDENTITY
e.g., "Developer ID Application: Name (TEAMID)"
APPLE_ID
Apple ID email
APPLE_PASSWORD
App-specific password
APPLE_TEAM_ID
Apple Developer Team ID
TAURI_SIGNING_PRIVATE_KEY
From
tauri signer generate
CLOUDFLARE_ACCOUNT_ID
Cloudflare account ID
CLOUDFLARE_R2_ACCESS_KEY_ID
R2 API access key
CLOUDFLARE_R2_SECRET_ACCESS_KEY
R2 API secret key
R2_BUCKET_NAME
R2 bucket name
OASIS_SERVER_URL
Your Oasis server URL
OASIS_CI_KEY
CI API key (
uk_live_*
)
密钥说明
APPLE_CERTIFICATE
Base64编码的.p12证书
APPLE_CERTIFICATE_PASSWORD
证书密码
APPLE_SIGNING_IDENTITY
例如:"Developer ID Application: Name (TEAMID)"
APPLE_ID
Apple ID邮箱
APPLE_PASSWORD
Apple专用密码
APPLE_TEAM_ID
Apple开发者团队ID
TAURI_SIGNING_PRIVATE_KEY
来自
tauri signer generate
命令
CLOUDFLARE_ACCOUNT_ID
Cloudflare账户ID
CLOUDFLARE_R2_ACCESS_KEY_ID
R2 API访问密钥
CLOUDFLARE_R2_SECRET_ACCESS_KEY
R2 API密钥
R2_BUCKET_NAME
R2存储桶名称
OASIS_SERVER_URL
你的Oasis服务器地址
OASIS_CI_KEY
CI API密钥(
uk_live_*

Supported Platforms

支持的平台

PlatformTargetBundle Types
macOS (Apple Silicon)
darwin-aarch64
.dmg, .app.tar.gz
macOS (Intel)
darwin-x86_64
.dmg, .app.tar.gz
Linux
linux-x86_64
.AppImage, .deb, .AppImage.tar.gz
Windows
windows-x86_64
.exe (NSIS), .nsis.zip
平台目标架构包类型
macOS(Apple Silicon)
darwin-aarch64
.dmg, .app.tar.gz
macOS(Intel)
darwin-x86_64
.dmg, .app.tar.gz
Linux
linux-x86_64
.AppImage, .deb, .AppImage.tar.gz
Windows
windows-x86_64
.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

常见问题

  1. API key prefix matters: Public keys start with
    pk_
    , CI keys with
    uk_live_
    . Using the wrong type will fail silently.
  2. Update endpoint URL format: Must include
    {{target}}
    and
    {{current_version}}
    placeholders exactly as shown.
  3. Signing key mismatch: The public key in
    tauri.conf.json
    must match the private key used in CI. Regenerating keys without updating both will break updates.
  4. Events not sending: Enable
    debug: true
    in SDK config to see network errors. Common causes: wrong serverUrl, CORS issues, invalid apiKey.
  5. Crashes not captured: Auto-capture only works if
    enableAutoCrashReporting: true
    is set during
    initOasis()
    , not after.
  6. macOS signing failures:
    APPLE_SIGNING_IDENTITY
    must match the certificate name exactly, including the team ID in parentheses.
  7. Release not appearing in app: Check
    auto_publish: true
    in workflow. Unpublished releases exist in dashboard but aren't served to clients.
  8. dry_run for testing: Use
    dry_run: true
    workflow input to test builds without uploading artifacts or registering releases.
  9. R2 public URL: The
    r2_public_url
    must be the public-facing CDN URL, not the R2 API endpoint.
  10. Version format:
    appVersion
    must be valid semver (e.g., "1.0.0", not "v1.0.0"). The workflow extracts version from git tags automatically.
  1. API密钥前缀很重要:公开密钥以
    pk_
    开头,CI密钥以
    uk_live_
    开头。使用错误类型的密钥会静默失败。
  2. 更新端点URL格式:必须严格包含
    {{target}}
    {{current_version}}
    占位符。
  3. 签名密钥不匹配
    tauri.conf.json
    中的公钥必须与CI中使用的私钥匹配。重新生成密钥后未同时更新两者会导致更新失败。
  4. 事件无法发送:在SDK配置中启用
    debug: true
    查看网络错误。常见原因:serverUrl错误、CORS问题、无效apiKey。
  5. 崩溃未被捕获:自动捕获仅在
    initOasis()
    时设置
    enableAutoCrashReporting: true
    才会生效,后续设置无效。
  6. macOS签名失败
    APPLE_SIGNING_IDENTITY
    必须与证书名称完全匹配,包括括号中的团队ID。
  7. 发布未在应用中显示:检查工作流中的
    auto_publish: true
    是否设置。未发布的版本仅存在于管理面板中,不会推送给客户端。
  8. 测试用dry_run:使用工作流输入
    dry_run: true
    可测试构建,而无需上传制品或注册版本。
  9. R2公开URL
    r2_public_url
    必须是公开可访问的CDN地址,而非R2 API端点。
  10. 版本格式
    appVersion
    必须是有效的语义化版本(如"1.0.0",而非"v1.0.0")。工作流会自动从Git标签中提取版本。