初始化

This commit is contained in:
李琦
2026-01-15 13:51:44 +08:00
commit b7b6d3e39e
156 changed files with 38913 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
package runner
import (
"bytes"
"context"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/google/uuid"
)
type GoRunner struct {
TempDir string
}
func (r *GoRunner) Run(ctx context.Context, code string) (*ExecutionResult, error) {
// 创建唯一的工作目录
workDir := filepath.Join(r.TempDir, uuid.New().String())
if err := os.MkdirAll(workDir, 0755); err != nil {
return nil, err
}
defer os.RemoveAll(workDir) // 清理
// 写入main.go
// 注意这里假设代码是一个完整的package main
// 如果用户只提供了函数片段,可能需要包装。这里假设是完整代码。
filePath := filepath.Join(workDir, "main.go")
if err := ioutil.WriteFile(filePath, []byte(code), 0644); err != nil {
return nil, err
}
// 准备命令
// 使用 go run 运行
cmd := exec.CommandContext(ctx, "go", "run", "main.go")
cmd.Dir = workDir
// 捕获输出
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
startTime := time.Now()
err := cmd.Run()
duration := time.Since(startTime).Milliseconds()
result := &ExecutionResult{
Output: stdout.String(),
Error: stderr.String(),
Duration: duration,
}
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
result.ExitCode = exitErr.ExitCode()
} else {
result.ExitCode = -1
}
// 如果是超时
if ctx.Err() == context.DeadlineExceeded {
result.Error += "\nExecution timed out"
}
}
return result, nil
}

View File

@@ -0,0 +1,60 @@
package runner
import (
"bytes"
"context"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/google/uuid"
)
type NodeRunner struct {
TempDir string
}
func (r *NodeRunner) Run(ctx context.Context, code string) (*ExecutionResult, error) {
workDir := filepath.Join(r.TempDir, uuid.New().String())
if err := os.MkdirAll(workDir, 0755); err != nil {
return nil, err
}
defer os.RemoveAll(workDir)
filePath := filepath.Join(workDir, "script.js")
if err := ioutil.WriteFile(filePath, []byte(code), 0644); err != nil {
return nil, err
}
cmd := exec.CommandContext(ctx, "node", "script.js")
cmd.Dir = workDir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
startTime := time.Now()
err := cmd.Run()
duration := time.Since(startTime).Milliseconds()
result := &ExecutionResult{
Output: stdout.String(),
Error: stderr.String(),
Duration: duration,
}
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
result.ExitCode = exitErr.ExitCode()
} else {
result.ExitCode = -1
}
if ctx.Err() == context.DeadlineExceeded {
result.Error += "\nExecution timed out"
}
}
return result, nil
}

View File

@@ -0,0 +1,60 @@
package runner
import (
"bytes"
"context"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/google/uuid"
)
type PHPRunner struct {
TempDir string
}
func (r *PHPRunner) Run(ctx context.Context, code string) (*ExecutionResult, error) {
workDir := filepath.Join(r.TempDir, uuid.New().String())
if err := os.MkdirAll(workDir, 0755); err != nil {
return nil, err
}
defer os.RemoveAll(workDir)
filePath := filepath.Join(workDir, "script.php")
if err := ioutil.WriteFile(filePath, []byte(code), 0644); err != nil {
return nil, err
}
cmd := exec.CommandContext(ctx, "php", "script.php")
cmd.Dir = workDir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
startTime := time.Now()
err := cmd.Run()
duration := time.Since(startTime).Milliseconds()
result := &ExecutionResult{
Output: stdout.String(),
Error: stderr.String(),
Duration: duration,
}
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
result.ExitCode = exitErr.ExitCode()
} else {
result.ExitCode = -1
}
if ctx.Err() == context.DeadlineExceeded {
result.Error += "\nExecution timed out"
}
}
return result, nil
}

40
server/runner/runner.go Normal file
View File

@@ -0,0 +1,40 @@
package runner
import (
"context"
"fmt"
"os"
"path/filepath"
)
// ExecutionResult 执行结果
type ExecutionResult struct {
Output string `json:"output"`
Error string `json:"error"`
Duration int64 `json:"duration"` // 毫秒
ExitCode int `json:"exitCode"`
}
// CodeRunner 代码执行接口
type CodeRunner interface {
Run(ctx context.Context, code string) (*ExecutionResult, error)
}
// GetRunner 获取对应语言的运行器
func GetRunner(language string) (CodeRunner, error) {
tempDir := filepath.Join(os.TempDir(), "art-code-runner")
if err := os.MkdirAll(tempDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create temp dir: %v", err)
}
switch language {
case "go", "golang":
return &GoRunner{TempDir: tempDir}, nil
case "php":
return &PHPRunner{TempDir: tempDir}, nil
case "javascript", "js", "node":
return &NodeRunner{TempDir: tempDir}, nil
default:
return nil, fmt.Errorf("unsupported language: %s", language)
}
}