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
7 changes: 5 additions & 2 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,14 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: latest
# pnpm 10+ ignores dependency build scripts by default (ERR_PNPM_IGNORED_BUILDS)
version: 9
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'pnpm'

- name: install Rust stable
Expand All @@ -111,6 +112,8 @@ jobs:

- name: Install dependencies
run: pnpm install
env:
HUSKY: '0'

- name: Build Tauri App (macOS)
if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-14'
Expand Down
55 changes: 47 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
name: Release
run-name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || '' }}
run-name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || (inputs.version || 'Manual Release') }}

on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., v1.0.0). If empty, uses package.json version'
required: false
Comment on lines +7 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): version 这个 workflow 输入目前只用于 run-name,而不会用来决定实际的发布/标签版本。

这种行为与输入描述不符,也可能会让用户误以为 version 会控制发布/标签版本,而事实并非如此。请考虑要么将 inputs.version 接入 Get Version 逻辑(在其被设置时优先使用),要么更新描述,使其准确表达版本始终来自 package.json

Suggested change
version:
description: 'Release version (e.g., v1.0.0). If empty, uses package.json version'
required: false
version:
description: 'Optional label for this manual release run (used in run name only). Release/tag version always comes from package.json.'
required: false
Original comment in English

suggestion (bug_risk): The version workflow input is only used for the run-name and not for determining the actual release/tag version.

This behavior doesn’t match the input description and may suggest to users that version controls the release/tag version when it doesn’t. Please either route inputs.version into the Get Version logic (preferring it when set) or update the description so it accurately reflects that the version always comes from package.json.

Suggested change
version:
description: 'Release version (e.g., v1.0.0). If empty, uses package.json version'
required: false
version:
description: 'Optional label for this manual release run (used in run name only). Release/tag version always comes from package.json.'
required: false

push:
tags:
- 'v*'
Expand All @@ -12,7 +16,7 @@ permissions:

jobs:
publish-tauri:
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
Expand All @@ -34,17 +38,20 @@ jobs:
name: Build ${{ matrix.display_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: latest
# pnpm 10+ ignores dependency build scripts by default (ERR_PNPM_IGNORED_BUILDS)
version: 9
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'pnpm'

- name: Get Version (Windows)
Expand All @@ -63,6 +70,18 @@ jobs:
VERSION=$(node -p "require('./package.json').version")
echo "version=v$VERSION" >> $GITHUB_OUTPUT

- name: Ensure git tag exists locally
shell: bash
run: |
VERSION="${{ matrix.platform == 'windows-latest' && steps.get_version_windows.outputs.version || steps.get_version_unix.outputs.version }}"
git fetch --tags --depth=1 origin "refs/tags/${VERSION}:refs/tags/${VERSION}" 2>/dev/null || true
if ! git rev-parse "$VERSION" >/dev/null 2>&1; then
git tag "$VERSION"
echo "Created local tag $VERSION"
else
echo "Tag $VERSION already exists"
fi

- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -80,9 +99,11 @@ jobs:

- name: Install dependencies
run: pnpm install
env:
HUSKY: '0'

- name: Create Release
if: startsWith(github.ref, 'refs/tags/v') && matrix.platform == 'macos-14'
if: (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && matrix.platform == 'macos-14'
id: create_release
uses: softprops/action-gh-release@v1
env:
Expand All @@ -107,7 +128,7 @@ jobs:
includeUpdaterJson: true

publish-webview2-fixed:
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
Expand All @@ -125,17 +146,20 @@ jobs:
name: Build ${{ matrix.display_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: latest
# pnpm 10+ ignores dependency build scripts by default (ERR_PNPM_IGNORED_BUILDS)
version: 9
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'pnpm'

- name: Get Version
Expand All @@ -145,6 +169,19 @@ jobs:
$VERSION = (node -p "require('./package.json').version")
echo "version=v$VERSION" >> $env:GITHUB_OUTPUT

- name: Ensure git tag exists locally
shell: pwsh
run: |
$version = "${{ steps.get_version.outputs.version }}"
git fetch --tags --depth=1 origin "refs/tags/${version}:refs/tags/${version}" 2>$null
$null = git rev-parse "$version" 2>$null
if ($LASTEXITCODE -ne 0) {
git tag "$version"
Write-Host "Created local tag $version"
} else {
Write-Host "Tag $version already exists"
}

- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -157,6 +194,8 @@ jobs:

- name: Install dependencies
run: pnpm install
env:
HUSKY: '0'

- name: Prepare WebView2 Fixed Runtime
shell: pwsh
Expand Down
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,14 @@
"eslint --fix",
"prettier --write"
]
},
"pnpm": {
"onlyBuiltDependencies": [
"@parcel/watcher",
"esbuild",
"optipng-bin",
"sharp",
"zopflipng-bin"
]
}
}
21 changes: 21 additions & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 编译产物
cursorpool-server
*.exe
*.dll
*.so
*.dylib

# 数据库文件
data/
*.db
*.db-journal
*.sqlite
*.sqlite3

# IDE
.idea/
.vscode/
*.swp

# 日志
*.log
37 changes: 37 additions & 0 deletions server/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package config

import "os"

// Config 全局配置
type Config struct {
Port string
JWTSecret string
DBPath string
APIPrefix string // 路由前缀,客户端请求 /api/xxx,这里反代去掉 /api
SMTPHost string
SMTPPort string
SMTPUser string
SMTPPass string
}

var App Config

func Load() {
App = Config{
Port: getEnv("PORT", "8787"),
JWTSecret: getEnv("JWT_SECRET", "cursor-pool-demo-secret-change-me"),
DBPath: getEnv("DB_PATH", "./data/cursorpool.db"),
APIPrefix: getEnv("API_PREFIX", ""), // 直接挂在根路径,如需 /api 前缀由反代处理
SMTPHost: getEnv("SMTP_HOST", ""),
SMTPPort: getEnv("SMTP_PORT", "465"),
SMTPUser: getEnv("SMTP_USER", ""),
SMTPPass: getEnv("SMTP_PASS", ""),
}
}

func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
121 changes: 121 additions & 0 deletions server/database/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package database

import (
"log"
"os"
"path/filepath"
"time"

"cursorpool-server/config"
"cursorpool-server/models"

"golang.org/x/crypto/bcrypt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

var DB *gorm.DB

// Init 初始化数据库并自动建表 + 写入种子数据
func Init() {
// 确保 data 目录存在
if dir := filepath.Dir(config.App.DBPath); dir != "" {
if err := os.MkdirAll(dir, 0755); err != nil {
log.Fatalf("创建数据库目录失败: %v", err)
}
}

db, err := gorm.Open(sqlite.Open(config.App.DBPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Warn),
})
if err != nil {
log.Fatalf("连接数据库失败: %v", err)
}
DB = db

// 自动建表(对齐客户端 types.rs 字段)
if err := db.AutoMigrate(
&models.User{},
&models.CursorAccount{},
&models.ActivationCode{},
&models.Article{},
&models.PublicInfo{},
); err != nil {
log.Fatalf("自动建表失败: %v", err)
}

seed(db)
}

// seed 写入演示数据,便于首次启动直接测试
func seed(db *gorm.DB) {
// 默认账号(密码 123456)
var userCount int64
db.Model(&models.User{}).Count(&userCount)
if userCount == 0 {
hash, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
db.Create(&models.User{
Email: "demo@cursorpool.test",
Password: string(hash),
Username: "demo",
Level: 1,
TotalCount: 100,
ExpireTime: time.Now().Add(365 * 24 * time.Hour),
})
}

// 默认 Cursor 账户池
var accCount int64
db.Model(&models.CursorAccount{}).Count(&accCount)
if accCount == 0 {
db.Create(&models.CursorAccount{
Email: "cursor-account-1@example.com",
Password: "dummy",
Token: "demo-cursor-token-1",
UsageCount: 0,
Status: 1,
})
}

// 默认激活码
var codeCount int64
db.Model(&models.ActivationCode{}).Count(&codeCount)
if codeCount == 0 {
db.Create(&models.ActivationCode{
Code: "DEMO2024",
Type: 1,
Name: "体验码",
Level: 1,
Duration: 30,
MaxUses: 1000,
UsedCount: 0,
Status: 1,
})
}

// 默认公告
var artCount int64
db.Model(&models.Article{}).Count(&artCount)
if artCount == 0 {
db.Create(&models.Article{
Title: "欢迎使用 Cursor Pool",
Content: "这是后端演示数据。请根据业务需求修改 handlers 目录下的实现。",
})
}

// 默认公告信息
var pubCount int64
db.Model(&models.PublicInfo{}).Count(&pubCount)
if pubCount == 0 {
db.Create(&models.PublicInfo{
Type: "info",
Closeable: true,
Title: "系统公告",
Description: "Cursor Pool 后端服务已启动",
ActionText: "前往官网",
ActionURL: "https://pool.52ai.org",
ActionType: "link",
})
}
}
Loading