oasis-server-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOasis 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@mainOasis是一款为Tauri应用打造的自托管版本管理与分析服务器,提供更新清单、崩溃报告和反馈收集功能。
服务器仓库:https://github.com/porkytheblack/oasis
可复用工作流:
porkytheblack/oasis/.github/workflows/tauri-release.yml@mainArchitecture 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.keybash
npx @tauri-apps/cli signer generate -w ~/.tauri/keys/your-app.keyOutput: Public key + private key file
Output: Public key + private key file
undefinedundefined2. 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/sdkbash
pnpm add @oasis/sdk5. 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端点
| Endpoint | Method | Purpose |
|---|---|---|
| GET | Update manifest |
| POST | Submit feedback |
| POST | Report crashes |
| POST | Register release (CI only) |
| 端点 | 方法 | 用途 |
|---|---|---|
| GET | 更新清单 |
| POST | 提交反馈 |
| POST | 上报崩溃 |
| 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 Contentjson
{
"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 ContentTemplate Variables
模板变量
| Variable | Description | Example |
|---|---|---|
| Operating system | |
| CPU architecture | |
| App version | |
| 变量 | 描述 | 示例 |
|---|---|---|
| 操作系统 | |
| CPU架构 | |
| 应用版本 | |
API Keys
API密钥
| Key Type | Format | Purpose |
|---|---|---|
| Public Key | | SDK operations (client-side) |
| CI Key | | Release registration (server-side) |
Example:
pk_coco_a1b2c3d4e5f6g7h8| 密钥类型 | 格式 | 用途 |
|---|---|---|
| 公钥 | | SDK操作(客户端) |
| CI密钥 | | 版本注册(服务端) |
示例:
pk_coco_a1b2c3d4e5f6g7h8SDK 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配置选项
| Option | Type | Default | Description |
|---|---|---|---|
| | required | Public API key |
| | required | Oasis server URL |
| | required | Current app version |
| | | Catch uncaught errors |
| | | Breadcrumb history limit |
| | | Request timeout (ms) |
| | | Enable debug logging |
| | - | Filter/modify events |
| | - | Error callback |
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| | 必填 | 公API密钥 |
| | 必填 | Oasis服务器URL |
| | 必填 | 当前应用版本 |
| | | 捕获未处理错误 |
| | | 面包屑历史记录上限 |
| | | 请求超时时间(毫秒) |
| | | 启用调试日志 |
| | - | 过滤/修改事件 |
| | - | 错误回调 |
Required Secrets
所需密钥
| Secret | Description |
|---|---|
| Base URL (e.g., |
| CI key for release registration |
| Public SDK key (exposed to client) |
| Public server URL (exposed to client) |
| Update signing key |
| Signing key password |
| 密钥 | 描述 |
|---|---|
| 基础URL(例如: |
| 用于版本注册的CI密钥 |
| 公开SDK密钥(暴露给客户端) |
| 公开服务器URL(暴露给客户端) |
| 更新签名密钥 |
| 签名密钥密码 |
Common Gotchas
常见注意事项
- Public key in tauri.conf.json must match private key — If you regenerate keys, update both the config and the GitHub secret.
- Endpoint URL uses double braces — not
{{target}}. Single braces are for workflow variables.{target} - App slug must be consistent — Use the same slug in workflow config, SDK init, and server registration.
- NEXT_PUBLIC_ prefix exposes to client — Oasis public keys are safe to expose; CI keys are not.
- Version string must match semver — Use , not
0.1.0in code. Tags usev0.1.0prefix.v - SDK init before crash reporting — Call early in app lifecycle to catch startup crashes.
initOasis() - HTTP 204 means no update — Don't treat this as an error; it's the expected response when current.
- Signature verification happens client-side — Tauri verifies signatures using the embedded pubkey.
- R2 URLs must be publicly accessible — The CDN URL in manifests must be reachable without auth.
- beforeSend can drop events — Return to filter sensitive data from crash reports.
null
- tauri.conf.json中的公钥必须与私钥匹配——如果重新生成密钥,请同时更新配置文件和GitHub密钥。
- 端点URL使用双大括号——使用而非
{{target}}。单大括号用于工作流变量。{target} - 应用slug必须保持一致——在工作流配置、SDK初始化和服务器注册中使用相同的slug。
- NEXT_PUBLIC_前缀会暴露给客户端——Oasis公钥可以安全暴露;CI密钥则不行。
- 版本字符串必须符合语义化版本规范——代码中使用而非
0.1.0。标签使用v0.1.0前缀。v - SDK初始化需在崩溃报告之前——在应用生命周期早期调用以捕获启动时的崩溃。
initOasis() - HTTP 204表示无可用更新——不要将其视为错误;这是当前版本为最新时的预期响应。
- 签名验证在客户端进行——Tauri使用嵌入的公钥验证签名。
- R2 URL必须可公开访问——清单中的CDN URL无需认证即可访问。
- beforeSend可丢弃事件——返回以过滤崩溃报告中的敏感数据。
null