mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
6ea364e67d
Add token-based authentication for the Launcher's embedded Web Dashboard. - Ephemeral token generated in-memory each run (or via PICOCLAW_LAUNCHER_TOKEN env var) - HMAC-SHA256 session cookie (HttpOnly, SameSite=Lax, Secure when HTTPS) - Bearer token support for API/script access - Rate limiting on login (10 attempts/IP/min) - Referrer-Policy: no-referrer on all responses - POST-only logout with JSON content-type (CSRF-safe) - System tray "Copy dashboard token" action - Login page shows contextual help (console/tray/log file path) - Path traversal protection via path.Clean - X-Forwarded-Host/Port/Proto support for reverse proxy deployments - Full i18n support (English, Chinese) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
3.8 KiB
Go
127 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Language represents the supported languages
|
|
type Language string
|
|
|
|
const (
|
|
LanguageEnglish Language = "en"
|
|
LanguageChinese Language = "zh"
|
|
)
|
|
|
|
// current language (default: English)
|
|
var currentLang Language = LanguageEnglish
|
|
|
|
// TranslationKey represents a translation key used for i18n
|
|
type TranslationKey string
|
|
|
|
const (
|
|
AppTooltip TranslationKey = "AppTooltip"
|
|
MenuOpen TranslationKey = "MenuOpen"
|
|
MenuOpenTooltip TranslationKey = "MenuOpenTooltip"
|
|
MenuCopyToken TranslationKey = "MenuCopyToken"
|
|
MenuCopyTokenHint TranslationKey = "MenuCopyTokenHint"
|
|
MenuAbout TranslationKey = "MenuAbout"
|
|
MenuAboutTooltip TranslationKey = "MenuAboutTooltip"
|
|
MenuVersion TranslationKey = "MenuVersion"
|
|
MenuVersionTooltip TranslationKey = "MenuVersionTooltip"
|
|
MenuGitHub TranslationKey = "MenuGitHub"
|
|
MenuDocs TranslationKey = "MenuDocs"
|
|
MenuRestart TranslationKey = "MenuRestart"
|
|
MenuRestartTooltip TranslationKey = "MenuRestartTooltip"
|
|
MenuQuit TranslationKey = "MenuQuit"
|
|
MenuQuitTooltip TranslationKey = "MenuQuitTooltip"
|
|
Exiting TranslationKey = "Exiting"
|
|
DocUrl TranslationKey = "DocUrl"
|
|
)
|
|
|
|
// Translation tables
|
|
// Chinese translations intentionally contain Han script
|
|
//
|
|
//nolint:gosmopolitan
|
|
var translations = map[Language]map[TranslationKey]string{
|
|
LanguageEnglish: {
|
|
AppTooltip: "%s - Web Console",
|
|
MenuOpen: "Open Console",
|
|
MenuOpenTooltip: "Open PicoClaw console in browser",
|
|
MenuCopyToken: "Copy dashboard token",
|
|
MenuCopyTokenHint: "Copy the current web console access token to the clipboard",
|
|
MenuAbout: "About",
|
|
MenuAboutTooltip: "About PicoClaw",
|
|
MenuVersion: "Version: %s",
|
|
MenuVersionTooltip: "Current version number",
|
|
MenuGitHub: "GitHub",
|
|
MenuDocs: "Documentation",
|
|
MenuRestart: "Restart Service",
|
|
MenuRestartTooltip: "Restart Gateway service",
|
|
MenuQuit: "Quit",
|
|
MenuQuitTooltip: "Exit PicoClaw",
|
|
Exiting: "Exiting PicoClaw...",
|
|
DocUrl: "https://docs.picoclaw.io/docs/",
|
|
},
|
|
LanguageChinese: {
|
|
AppTooltip: "%s - Web Console",
|
|
MenuOpen: "打开控制台",
|
|
MenuOpenTooltip: "在浏览器中打开 PicoClaw 控制台",
|
|
MenuCopyToken: "复制控制台口令",
|
|
MenuCopyTokenHint: "将当前 Web 控制台访问口令复制到剪贴板",
|
|
MenuAbout: "关于",
|
|
MenuAboutTooltip: "关于 PicoClaw",
|
|
MenuVersion: "版本: %s",
|
|
MenuVersionTooltip: "当前版本号",
|
|
MenuGitHub: "GitHub",
|
|
MenuDocs: "文档",
|
|
MenuRestart: "重启服务",
|
|
MenuRestartTooltip: "重启核心服务",
|
|
MenuQuit: "退出",
|
|
MenuQuitTooltip: "退出 PicoClaw",
|
|
Exiting: "正在退出 PicoClaw...",
|
|
DocUrl: "https://docs.picoclaw.io/zh-Hans/docs/",
|
|
},
|
|
}
|
|
|
|
// SetLanguage sets the current language
|
|
func SetLanguage(lang string) {
|
|
lang = strings.ToLower(strings.TrimSpace(lang))
|
|
|
|
// Extract language code before first underscore or dot
|
|
// e.g., "en_US.UTF-8" -> "en", "zh_CN" -> "zh"
|
|
if idx := strings.IndexAny(lang, "_."); idx > 0 {
|
|
lang = lang[:idx]
|
|
}
|
|
|
|
if lang == "zh" || lang == "zh-cn" || lang == "chinese" {
|
|
currentLang = LanguageChinese
|
|
} else {
|
|
currentLang = LanguageEnglish
|
|
}
|
|
}
|
|
|
|
// GetLanguage returns the current language
|
|
func GetLanguage() Language {
|
|
return currentLang
|
|
}
|
|
|
|
// T translates a key to the current language
|
|
func T(key TranslationKey, args ...any) string {
|
|
if trans, ok := translations[currentLang][key]; ok {
|
|
if len(args) > 0 {
|
|
return fmt.Sprintf(trans, args...)
|
|
}
|
|
return trans
|
|
}
|
|
return string(key)
|
|
}
|
|
|
|
// Initialize i18n from environment variable
|
|
func init() {
|
|
if lang := os.Getenv("LANG"); lang != "" {
|
|
SetLanguage(lang)
|
|
}
|
|
}
|