Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ dist-ssr
others/
src-tauri/.wrangler
.env
LOCAL_DEV.md
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# CF Studio

Simplified Chinese README: [README.zh-CN.md](README.zh-CN.md).

A blazing-fast, native desktop client for Cloudflare D1 and R2.

[Website](https://cfstudio.dev) • [Portfolio](https://mubashar.dev) • [YouTube](https://youtube.com/@mubashardev)
Expand Down
92 changes: 92 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# CF Studio MakerJackie Fork

这是 `makerjackie/cf-studio` 的中文说明。这个 fork 基于 `mubashardev/cf-studio`,目标是评估它是否适合作为本地 Cloudflare 管理器的基础。

## 这个 fork 改了什么

- 修复 macOS GUI 启动时检测不到 nvm 中 `node` / `wrangler` 的问题。
- 增加基础中英文界面切换。
- D1 / R2 / KV 主要页面已接入中文文案。
- 优先读取 `CLOUDFLARE_API_TOKEN` 环境变量,再回退到 Wrangler OAuth 配置。
- 用公开 fallback 替换上游私有 `src/pro_modules` submodule,让仓库可以直接 clone、安装、构建。

## 功能范围

当前公开 fork 适合用来评估这些工作流:

- D1 数据库列表
- D1 表结构查看
- D1 表数据浏览
- D1 SQL 查询编辑器
- D1 可视化结构图
- R2 存储桶和对象列表
- KV 占位页

注意:这个 fork 不包含上游私有 Pro 模块。高级导出、完整 R2 上传下载、审计能力等功能需要单独实现或继续接入。

## 本地开发

准备环境:

```bash
brew install bun
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
npm install -g wrangler
wrangler login
```

启动:

```bash
git clone git@github.com:makerjackie/cf-studio.git
cd cf-studio
export PATH="$HOME/.bun/bin:$PATH"
source "$HOME/.cargo/env"
bun install --frozen-lockfile
bun run tauri dev
```

如果你使用 API Token,而不是 `wrangler login`:

```bash
export CLOUDFLARE_API_TOKEN="your-token"
export CLOUDFLARE_ACCOUNT_ID="your-account-id"
bun run tauri dev
```

## 构建本机 App

```bash
export PATH="$HOME/.bun/bin:$PATH"
source "$HOME/.cargo/env"
bun run tauri build
```

macOS app 通常会生成在:

```bash
src-tauri/target/release/bundle/macos/CF-Studio.app
```

安装到 `/Applications`:

```bash
cp -R "src-tauri/target/release/bundle/macos/CF-Studio.app" /Applications/
```

如果 macOS 阻止打开本地构建版本,可以移除 quarantine 标记:

```bash
xattr -dr com.apple.quarantine /Applications/CF-Studio.app
```

## 当前判断

这个 fork 更适合作为“Cloudflare Dashboard / Wrangler / cf CLI 的本地伴侣”,而不是完整替代官网。短期更值得投入的方向是:

- 让 D1 表格浏览和 SQL 编辑更稳定。
- 补齐 R2 上传、下载、预览、复制公开 URL。
- 做 Cloudflare Token 权限检测。
- 增加本地 Wrangler / Local Explorer / 远程资源对比。
- 把中文和英文文案整理成更完整的 i18n 结构。

176 changes: 176 additions & 0 deletions docs/makerjackie-fork-requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# MakerJackie CF Studio Fork Requirements

## Goal

Create a lightweight MakerJackie fork of CF Studio for daily Cloudflare D1 and R2 work.

This fork is not trying to replace the Cloudflare dashboard. It should make repeated local desktop workflows faster, especially when the dashboard is slow or too heavy for a quick D1/R2 check.

## Scope for this first pass

1. Make the app reliably detect Node.js, npm, npx, and Wrangler when they are installed through nvm on macOS.
2. Add a basic language system with English and Simplified Chinese support.
3. Keep the public clone runnable without private Pro modules.
4. Start the local Tauri app and verify the setup screen no longer blocks when nvm provides the tools.

## Non-goals

- Do not build a full Cloudflare dashboard clone.
- Do not depend on the new `cf` CLI as the backend.
- Do not add full KV management in this pass.
- Do not implement Local Explorer integration in this pass.
- Do not rewrite the UI architecture.

## Background

Cloudflare's current tool split is:

- Wrangler remains the mature developer CLI.
- Cloudflare Dashboard remains the complete remote management surface.
- Local Explorer is useful for local Wrangler dev state, including local D1, R2, KV, Durable Objects SQLite, and Workflows.
- The new `cf` CLI is promising but still a technical preview.
- CF Studio is useful as a focused remote D1/R2 desktop client.

The fork should lean into the focused desktop-client role.

## Requirement 1, nvm-aware dependency detection

### Problem

On macOS, Tauri apps launched from Finder do not reliably inherit the interactive shell PATH.

The user's terminal can resolve:

```bash
which node
which wrangler
```

But the app may still report Node.js or Wrangler as missing.

### Expected behavior

When the app checks dependencies, it should detect binaries installed through nvm:

```txt
~/.nvm/versions/node/*/bin/node
~/.nvm/versions/node/*/bin/npm
~/.nvm/versions/node/*/bin/npx
~/.nvm/versions/node/*/bin/wrangler
```

It should also check:

```txt
~/.npm-global/bin
/opt/homebrew/bin
/usr/local/bin
```

### Implementation direction

In Rust:

- Add a reusable shell environment helper.
- Load `~/.nvm/nvm.sh` when present.
- Prepend common nvm, npm-global, and Homebrew bin folders before probing commands.
- Use the same shell bootstrap for silent Wrangler refresh commands.

### Acceptance checks

From a normal terminal:

```bash
npm run tauri dev
```

The setup wizard should mark Node.js / npm and Cloudflare Wrangler as installed when they exist under nvm.

## Requirement 2, basic i18n

### Problem

The app is English-only. For personal daily use, the main navigation and setup/settings surfaces should support Chinese.

### Expected behavior

The app should include:

- English, `en-US`
- Simplified Chinese, `zh-CN`
- A language selector in Settings
- Persisted language preference in localStorage

### Initial translation scope

Translate only high-frequency surfaces in this pass:

- Setup wizard
- Sidebar navigation
- Top title labels
- Settings page headings and tabs
- Common empty-state and coming-soon labels where easy

Do not try to translate every D1/R2 table cell and every Pro/hidden feature in the first pass.

## Requirement 3, public clone runnable

### Problem

The upstream repository imports `src/pro_modules`, but that folder is ignored and absent in a public clone.

### Expected behavior

The MakerJackie fork should run locally without private Pro modules.

### Implementation direction

Add public fallback modules under `src/pro_modules` and adjust `.gitignore` so these fallback files are tracked.

Fallback behavior:

- Remote config defaults to disabled paid features.
- R2 Buckets view remains usable for bucket listing and object listing with the public backend commands.
- Pro-only actions show disabled state or a clear message.
- Audit and query-history views render placeholder screens.

## Requirement 4, keep changes small

This first fork should stay close to upstream.

Avoid:

- Big design rewrites
- Deep R2 upload/download refactors
- New auth systems
- `cf` CLI integration
- Local Explorer integration

## Future ideas

Potential follow-up work:

- R2 image hosting workflow, upload, compress, copy Markdown URL
- D1 local/remote diff
- D1 seed and backup helpers
- KV JSON search and editor
- Cloudflare API token permission checker
- Local Explorer API companion view

## Verification

Run:

```bash
npm install
npm run build
npm run tauri dev
```

Expected result:

- TypeScript build passes.
- Rust build passes or surfaces only environment-specific toolchain issues.
- App starts locally.
- Setup wizard sees nvm-provided Node/npm/Wrangler.
- Settings exposes language selection.
31 changes: 26 additions & 5 deletions src-tauri/src/cloudflare_auth.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// cloudflare_auth.rs
//
// Reads the local Wrangler OAuth config to provide zero-touch authentication.
// No API tokens are ever stored in CF Studio — we reuse the session that
// `wrangler login` already created on the user's machine.
// When the user already exports CLOUDFLARE_API_TOKEN for Wrangler, use that
// in-memory value first. No API tokens are stored by CF Studio.

use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

use crate::cloudflare_client::{CfError, CfResponse, CloudflareClient};
use crate::shell_env::{login_shell, with_user_path};

// ── Error type ─────────────────────────────────────────────────────────────────

Expand All @@ -26,7 +27,7 @@ pub enum AuthError {
#[error("Failed to parse Wrangler config TOML: {0}")]
TomlParse(#[from] toml::de::Error),

#[error("No oauth_token found in Wrangler config. Run `wrangler login` first.")]
#[error("No oauth_token found in Wrangler config and CLOUDFLARE_API_TOKEN is not set. Run `wrangler login` first.")]
NoToken,

#[error("Command execution failed: {0}")]
Expand Down Expand Up @@ -168,8 +169,22 @@ pub fn wrangler_config_path() -> Result<PathBuf, AuthError> {

// ── Core parsing logic ─────────────────────────────────────────────────────────

fn env_api_token() -> Option<String> {
std::env::var("CLOUDFLARE_API_TOKEN")
.ok()
.map(|token| token.trim().to_string())
.filter(|token| !token.is_empty())
}

/// Reads and parses the Wrangler config, returning the extracted credentials.
pub fn read_credentials() -> Result<CloudflareCredentials, AuthError> {
if let Some(api_token) = env_api_token() {
return Ok(CloudflareCredentials {
oauth_token: api_token,
account_id: std::env::var("CLOUDFLARE_ACCOUNT_ID").ok(),
});
}

let candidates = wrangler_candidate_paths();
if candidates.is_empty() {
return Err(AuthError::ConfigDirNotFound);
Expand Down Expand Up @@ -262,14 +277,20 @@ pub fn read_credentials() -> Result<CloudflareCredentials, AuthError> {
/// re-read the configuration file and return the fresh token.
#[tauri::command]
pub async fn refresh_wrangler_token() -> Result<CloudflareCredentials, AuthError> {
if env_api_token().is_some() {
return read_credentials();
}

let output = tokio::task::spawn_blocking(|| {
let mut cmd = if cfg!(target_os = "windows") {
let mut c = std::process::Command::new("cmd");
c.args(["/c", "npx", "wrangler", "d1", "list"]);
c
} else {
let mut c = std::process::Command::new("npx");
c.args(["wrangler", "d1", "list"]);
let (shell, login_flag) = login_shell();
let mut c = std::process::Command::new(shell);
let command = with_user_path("npx wrangler d1 list");
c.args([login_flag, "-c", &command]);
c
};

Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod cloudflare_client;
pub mod d1;
pub mod r2;
pub mod setup;
pub mod shell_env;
pub mod user;
pub mod db;

Expand Down
Loading