oasis-server-setup

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Oasis Server Setup — Development Guide

Oasis服务器搭建——开发指南

You are an expert on the Oasis update server. Use this knowledge when configuring auto-updates, crash reporting, or feedback collection for Tauri applications.
您是Oasis更新服务器方面的专家。在为Tauri应用配置自动更新、崩溃报告或反馈收集功能时,请运用这些知识。

What It Is

什么是Oasis

Oasis is a self-hosted release management and analytics server for Tauri applications. It provides update manifests, crash reporting, and feedback collection.
Server repo: https://github.com/porkytheblack/oasis Reusable workflow:
porkytheblack/oasis/.github/workflows/tauri-release.yml@main
Oasis是一款为Tauri应用打造的自托管版本管理与分析服务器,提供更新清单、崩溃报告和反馈收集功能。
服务器仓库https://github.com/porkytheblack/oasis 可复用工作流
porkytheblack/oasis/.github/workflows/tauri-release.yml@main

Architecture at a Glance

架构概览

┌─────────────────────────────────────────────────────────────────┐
│                      Tauri Desktop App                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │ Auto-Update │  │ Crash SDK   │  │ Feedback SDK            │  │
│  └──────┬──────┘  └──────┬──────┘  └───────────┬─────────────┘  │
└─────────┼────────────────┼─────────────────────┼────────────────┘
          │                │                     │
          ▼                ▼                     ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Oasis Server                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │ /update     │  │ /crashes    │  │ /feedback               │  │
│  │ manifests   │  │ collection  │  │ collection              │  │
│  └──────┬──────┘  └─────────────┘  └─────────────────────────┘  │
└─────────┼───────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                    Cloudflare R2 (CDN)                           │
│         Artifact storage: .dmg, .exe, .AppImage                  │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                      Tauri Desktop App                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │ Auto-Update │  │ Crash SDK   │  │ Feedback SDK            │  │
│  └──────┬──────┘  └──────┬──────┘  └───────────┬─────────────┘  │
└─────────┼────────────────┼─────────────────────┼────────────────┘
          │                │                     │
          ▼                ▼                     ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Oasis Server                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │ /update     │  │ /crashes    │  │ /feedback               │  │
│  │ manifests   │  │ collection  │  │ collection              │  │
│  └──────┬──────┘  └─────────────┘  └─────────────────────────┘  │
└─────────┼───────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                    Cloudflare R2 (CDN)                           │
│         Artifact storage: .dmg, .exe, .AppImage                  │
└─────────────────────────────────────────────────────────────────┘

Quick Start

快速开始

1. Generate signing keys

1. 生成签名密钥

bash
npx @tauri-apps/cli signer generate -w ~/.tauri/keys/your-app.key
bash
npx @tauri-apps/cli signer generate -w ~/.tauri/keys/your-app.key

Output: Public key + private key file

Output: Public key + private key file

undefined
undefined

2. Configure tauri.conf.json

2. 配置tauri.conf.json

json
{
  "plugins": {
    "updater": {
      "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6...",
      "endpoints": [
        "https://oasis.yourdomain.com/your-app/update/{{target}}-{{arch}}/{{current_version}}"
      ],
      "windows": {
        "installMode": "passive"
      }
    }
  }
}
json
{
  "plugins": {
    "updater": {
      "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6...",
      "endpoints": [
        "https://oasis.yourdomain.com/your-app/update/{{target}}-{{arch}}/{{current_version}}"
      ],
      "windows": {
        "installMode": "passive"
      }
    }
  }
}

3. Add updater capabilities

3. 添加更新器权限

json
// capabilities/default.json
{
  "permissions": [
    "updater:default",
    "updater:allow-check",
    "updater:allow-download-and-install",
    "process:default",
    "process:allow-restart"
  ]
}
json
// capabilities/default.json
{
  "permissions": [
    "updater:default",
    "updater:allow-check",
    "updater:allow-download-and-install",
    "process:default",
    "process:allow-restart"
  ]
}

4. Install Oasis SDK

4. 安装Oasis SDK

bash
pnpm add @oasis/sdk
bash
pnpm add @oasis/sdk

5. Initialize SDK

5. 初始化SDK

typescript
// lib/oasis.ts
import { initOasis } from '@oasis/sdk';

export const oasis = initOasis({
  apiKey: process.env.NEXT_PUBLIC_OASIS_API_KEY!,
  serverUrl: process.env.NEXT_PUBLIC_OASIS_SERVER_URL!,
  appVersion: '0.1.0',
  enableAutoCrashReporting: true,
});
typescript
// lib/oasis.ts
import { initOasis } from '@oasis/sdk';

export const oasis = initOasis({
  apiKey: process.env.NEXT_PUBLIC_OASIS_API_KEY!,
  serverUrl: process.env.NEXT_PUBLIC_OASIS_SERVER_URL!,
  appVersion: '0.1.0',
  enableAutoCrashReporting: true,
});

6. Check for updates

6. 检查更新

typescript
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';

async function checkForUpdates() {
  const update = await check();
  if (update?.available) {
    await update.downloadAndInstall();
    await relaunch();
  }
}
typescript
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';

async function checkForUpdates() {
  const update = await check();
  if (update?.available) {
    await update.downloadAndInstall();
    await relaunch();
  }
}

API Endpoints

API端点

EndpointMethodPurpose
/{app_slug}/update/{target}-{arch}/{version}
GETUpdate manifest
/sdk/{app_slug}/feedback
POSTSubmit feedback
/sdk/{app_slug}/crashes
POSTReport crashes
/api/releases/{app_slug}
POSTRegister release (CI only)
端点方法用途
/{app_slug}/update/{target}-{arch}/{version}
GET更新清单
/sdk/{app_slug}/feedback
POST提交反馈
/sdk/{app_slug}/crashes
POST上报崩溃
/api/releases/{app_slug}
POST注册版本(仅CI使用)

Update Manifest Response

更新清单响应

json
{
  "version": "0.2.0",
  "notes": "Bug fixes and improvements",
  "pub_date": "2024-01-15T10:00:00Z",
  "platforms": {
    "darwin-aarch64": {
      "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
      "url": "https://cdn.example.com/app/v0.2.0/App_aarch64.app.tar.gz"
    },
    "darwin-x86_64": {
      "signature": "...",
      "url": "https://cdn.example.com/app/v0.2.0/App_x64.app.tar.gz"
    },
    "windows-x86_64": {
      "signature": "...",
      "url": "https://cdn.example.com/app/v0.2.0/App_x64-setup.nsis.zip"
    },
    "linux-x86_64": {
      "signature": "...",
      "url": "https://cdn.example.com/app/v0.2.0/App_amd64.AppImage.tar.gz"
    }
  }
}
No update available returns
HTTP 204 No Content
.
json
{
  "version": "0.2.0",
  "notes": "Bug fixes and improvements",
  "pub_date": "2024-01-15T10:00:00Z",
  "platforms": {
    "darwin-aarch64": {
      "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
      "url": "https://cdn.example.com/app/v0.2.0/App_aarch64.app.tar.gz"
    },
    "darwin-x86_64": {
      "signature": "...",
      "url": "https://cdn.example.com/app/v0.2.0/App_x64.app.tar.gz"
    },
    "windows-x86_64": {
      "signature": "...",
      "url": "https://cdn.example.com/app/v0.2.0/App_x64-setup.nsis.zip"
    },
    "linux-x86_64": {
      "signature": "...",
      "url": "https://cdn.example.com/app/v0.2.0/App_amd64.AppImage.tar.gz"
    }
  }
}
无可用更新时返回
HTTP 204 No Content

Template Variables

模板变量

VariableDescriptionExample
{{target}}
Operating system
darwin
,
windows
,
linux
{{arch}}
CPU architecture
x86_64
,
aarch64
{{current_version}}
App version
0.1.0
变量描述示例
{{target}}
操作系统
darwin
,
windows
,
linux
{{arch}}
CPU架构
x86_64
,
aarch64
{{current_version}}
应用版本
0.1.0

API Keys

API密钥

Key TypeFormatPurpose
Public Key
pk_{app-slug}_{random}
SDK operations (client-side)
CI Key
ci_{app-slug}_{random}
Release registration (server-side)
Example:
pk_coco_a1b2c3d4e5f6g7h8
密钥类型格式用途
公钥
pk_{app-slug}_{random}
SDK操作(客户端)
CI密钥
ci_{app-slug}_{random}
版本注册(服务端)
示例:
pk_coco_a1b2c3d4e5f6g7h8

SDK Usage Patterns

SDK使用模式

Feedback Submission

反馈提交

typescript
// Categorized feedback
await oasis.feedback.submit({
  category: 'bug',        // 'bug' | 'feature' | 'general'
  message: 'Save button not working',
  email: 'user@example.com',
  metadata: { screen: 'settings' },
});

// Convenience methods
await oasis.feedback.reportBug('Description');
await oasis.feedback.requestFeature('Description');
await oasis.feedback.sendFeedback('Description');
typescript
// 分类反馈
await oasis.feedback.submit({
  category: 'bug',        // 'bug' | 'feature' | 'general'
  message: 'Save button not working',
  email: 'user@example.com',
  metadata: { screen: 'settings' },
});

// 便捷方法
await oasis.feedback.reportBug('Description');
await oasis.feedback.requestFeature('Description');
await oasis.feedback.sendFeedback('Description');

Crash Reporting

崩溃报告

typescript
// Manual capture
try {
  riskyOperation();
} catch (error) {
  await oasis.crashes.captureException(error, {
    appState: { screen: 'checkout' },
    severity: 'error',  // 'warning' | 'error' | 'fatal'
  });
}

// Toggle auto-capture
oasis.crashes.enableAutoCrashReporting();
oasis.crashes.disableAutoCrashReporting();
typescript
// 手动捕获
try {
  riskyOperation();
} catch (error) {
  await oasis.crashes.captureException(error, {
    appState: { screen: 'checkout' },
    severity: 'error',  // 'warning' | 'error' | 'fatal'
  });
}

// 切换自动捕获
oasis.crashes.enableAutoCrashReporting();
oasis.crashes.disableAutoCrashReporting();

Breadcrumbs

面包屑记录

typescript
oasis.breadcrumbs.addNavigation('/home', '/settings');
oasis.breadcrumbs.addClick('Save Button');
oasis.breadcrumbs.addHttp('POST', '/api/save', 200);
oasis.breadcrumbs.addCustom('wallet', 'Connected', { address: '0x...' });
typescript
oasis.breadcrumbs.addNavigation('/home', '/settings');
oasis.breadcrumbs.addClick('Save Button');
oasis.breadcrumbs.addHttp('POST', '/api/save', 200);
oasis.breadcrumbs.addCustom('wallet', 'Connected', { address: '0x...' });

User Context

用户上下文

typescript
// Set after authentication
oasis.setUser({
  id: 'user-123',
  email: 'user@example.com',
  username: 'johndoe',
});

// Clear on logout
oasis.setUser(null);
typescript
// 认证后设置
oasis.setUser({
  id: 'user-123',
  email: 'user@example.com',
  username: 'johndoe',
});

// 登出时清除
oasis.setUser(null);

SDK Configuration Options

SDK配置选项

OptionTypeDefaultDescription
apiKey
string
requiredPublic API key
serverUrl
string
requiredOasis server URL
appVersion
string
requiredCurrent app version
enableAutoCrashReporting
boolean
false
Catch uncaught errors
maxBreadcrumbs
number
50
Breadcrumb history limit
timeout
number
10000
Request timeout (ms)
debug
boolean
false
Enable debug logging
beforeSend
function
-Filter/modify events
onError
function
-Error callback
选项类型默认值描述
apiKey
string
必填公API密钥
serverUrl
string
必填Oasis服务器URL
appVersion
string
必填当前应用版本
enableAutoCrashReporting
boolean
false
捕获未处理错误
maxBreadcrumbs
number
50
面包屑历史记录上限
timeout
number
10000
请求超时时间(毫秒)
debug
boolean
false
启用调试日志
beforeSend
function
-过滤/修改事件
onError
function
-错误回调

Required Secrets

所需密钥

SecretDescription
OASIS_SERVER_URL
Base URL (e.g.,
https://oasis.yourdomain.com
)
OASIS_CI_KEY
CI key for release registration
NEXT_PUBLIC_OASIS_API_KEY
Public SDK key (exposed to client)
NEXT_PUBLIC_OASIS_SERVER_URL
Public server URL (exposed to client)
TAURI_SIGNING_PRIVATE_KEY
Update signing key
TAURI_SIGNING_PRIVATE_KEY_PASSWORD
Signing key password
密钥描述
OASIS_SERVER_URL
基础URL(例如:
https://oasis.yourdomain.com
OASIS_CI_KEY
用于版本注册的CI密钥
NEXT_PUBLIC_OASIS_API_KEY
公开SDK密钥(暴露给客户端)
NEXT_PUBLIC_OASIS_SERVER_URL
公开服务器URL(暴露给客户端)
TAURI_SIGNING_PRIVATE_KEY
更新签名密钥
TAURI_SIGNING_PRIVATE_KEY_PASSWORD
签名密钥密码

Common Gotchas

常见注意事项

  1. Public key in tauri.conf.json must match private key — If you regenerate keys, update both the config and the GitHub secret.
  2. Endpoint URL uses double braces
    {{target}}
    not
    {target}
    . Single braces are for workflow variables.
  3. App slug must be consistent — Use the same slug in workflow config, SDK init, and server registration.
  4. NEXT_PUBLIC_ prefix exposes to client — Oasis public keys are safe to expose; CI keys are not.
  5. Version string must match semver — Use
    0.1.0
    , not
    v0.1.0
    in code. Tags use
    v
    prefix.
  6. SDK init before crash reporting — Call
    initOasis()
    early in app lifecycle to catch startup crashes.
  7. HTTP 204 means no update — Don't treat this as an error; it's the expected response when current.
  8. Signature verification happens client-side — Tauri verifies signatures using the embedded pubkey.
  9. R2 URLs must be publicly accessible — The CDN URL in manifests must be reachable without auth.
  10. beforeSend can drop events — Return
    null
    to filter sensitive data from crash reports.
  1. tauri.conf.json中的公钥必须与私钥匹配——如果重新生成密钥,请同时更新配置文件和GitHub密钥。
  2. 端点URL使用双大括号——使用
    {{target}}
    而非
    {target}
    。单大括号用于工作流变量。
  3. 应用slug必须保持一致——在工作流配置、SDK初始化和服务器注册中使用相同的slug。
  4. NEXT_PUBLIC_前缀会暴露给客户端——Oasis公钥可以安全暴露;CI密钥则不行。
  5. 版本字符串必须符合语义化版本规范——代码中使用
    0.1.0
    而非
    v0.1.0
    。标签使用
    v
    前缀。
  6. SDK初始化需在崩溃报告之前——在应用生命周期早期调用
    initOasis()
    以捕获启动时的崩溃。
  7. HTTP 204表示无可用更新——不要将其视为错误;这是当前版本为最新时的预期响应。
  8. 签名验证在客户端进行——Tauri使用嵌入的公钥验证签名。
  9. R2 URL必须可公开访问——清单中的CDN URL无需认证即可访问。
  10. beforeSend可丢弃事件——返回
    null
    以过滤崩溃报告中的敏感数据。