1 parent
fca32c2233
commit
7f0e5b7b10
8 files changed
+21
-105
No files matched your search
+2
-2
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"up/utils"
|
||||
@@ -26,7 +27,6 @@ func ExtlinuxRead(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
// В случае ошибки кодирования уже ничего не сделать,
|
||||
// можно только залогировать (здесь опущено для краткости)
|
||||
fmt.Errorf("Encoding error: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
var MOUNT_POINT = os.Getenv("MOUNT_POINT")
|
||||
var EXTLINUX_FILE = os.Getenv("EXTLINUX_FILE")
|
||||
// Ограничиваем размер тела, чтобы избежать слишком больших запросов (аналог 4096 в C)
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -24,7 +23,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Парсим JSON
|
||||
var requestData struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
@@ -35,7 +33,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем наличие поля content
|
||||
if requestData.Content == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@@ -45,14 +42,12 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
timestamp := r.URL.Query().Get("timestamp")
|
||||
if timestamp != "" {
|
||||
// Полное URL-декодирование (в C декодировался только %3A, но здесь используем стандартный способ)
|
||||
decoded, err := url.QueryUnescape(timestamp)
|
||||
if err == nil {
|
||||
timestamp = decoded
|
||||
}
|
||||
}
|
||||
|
||||
// Монтируем устройство
|
||||
if utils.MountDevice() < 0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -60,11 +55,9 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Формируем полный путь к файлу
|
||||
filePath := filepath.Join(MOUNT_POINT, EXTLINUX_FILE)
|
||||
fmt.Println(filePath)
|
||||
|
||||
// Открываем файл на запись (создаём/перезаписываем)
|
||||
f, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -74,7 +67,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Записываем содержимое
|
||||
if _, err := f.WriteString(requestData.Content); err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -86,7 +78,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
versionPath := filepath.Join(MOUNT_POINT, EXTLINUX_FILE+".version")
|
||||
vf, err := os.Create(versionPath)
|
||||
if err == nil {
|
||||
// Форматируем ISO 8601 в человекочитаемый вид (как в C)
|
||||
var readableDate string
|
||||
if len(timestamp) >= 19 {
|
||||
readableDate = timestamp[:10] + " " + timestamp[11:19] // "YYYY-MM-DD HH:MM:SS"
|
||||
@@ -99,10 +90,8 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Принудительно сбрасываем файловые буферы на диск (аналог sync())
|
||||
syscall.Sync()
|
||||
|
||||
// Успешный ответ
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{"status":"success","message":"extlinux.conf updated"}`))
|
||||
|
||||
+1
-8
@@ -8,33 +8,26 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// handleFiledate обрабатывает HTTP-запрос для получения даты файла (версии)
|
||||
func FileDate(w http.ResponseWriter, r *http.Request) {
|
||||
var MOUNT_POINT = os.Getenv("MOUNT_POINT")
|
||||
// Получаем параметр "file" из строки запроса
|
||||
filename := r.URL.Query().Get("file")
|
||||
if filename == "" {
|
||||
http.Error(w, `{"error":"Missing file parameter"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Формируем путь к файлу версии: <mount_point>/<filename>.version
|
||||
versionPath := filepath.Join(MOUNT_POINT, filename+".version")
|
||||
|
||||
// Пытаемся прочитать первую строку из файла версии
|
||||
dateStr := ""
|
||||
f, err := os.Open(versionPath)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
if scanner.Scan() {
|
||||
dateStr = scanner.Text() // считываем строку без символа новой строки
|
||||
dateStr = scanner.Text()
|
||||
}
|
||||
// Ошибки сканера игнорируем (как в C)
|
||||
}
|
||||
// Если файл не существует или другая ошибка — оставляем dateStr пустой
|
||||
|
||||
// Формируем JSON-ответ
|
||||
response := map[string]interface{}{
|
||||
"status": "success",
|
||||
"date": dateStr,
|
||||
|
||||
+1
-13
@@ -10,13 +10,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// MOUNT_POINT должен быть определён (например, как глобальная константа)
|
||||
// const MOUNT_POINT = "/mnt/mydevice"
|
||||
|
||||
// handleReboot обрабатывает HTTP-запрос на перезагрузку системы
|
||||
func HandleReboot(w http.ResponseWriter, r *http.Request) {
|
||||
MOUNT_POINT := os.Getenv("MOUNT_POINT")
|
||||
// Отправляем JSON-ответ (как в C: mg_http_reply)
|
||||
response := map[string]string{
|
||||
"status": "success",
|
||||
"message": "System rebooting...",
|
||||
@@ -27,26 +22,19 @@ func HandleReboot(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Синхронизируем все файловые системы (аналог sync())
|
||||
syscall.Sync()
|
||||
|
||||
// Размонтируем точку монтирования, если она смонтирована
|
||||
fmt.Printf("Unmounting %s...\n", MOUNT_POINT)
|
||||
if err := syscall.Unmount(MOUNT_POINT, 0); err != nil {
|
||||
// Ошибку не фатализируем, просто логируем
|
||||
fmt.Printf("Unmount error: %v\n", err)
|
||||
}
|
||||
|
||||
// Повторная синхронизация
|
||||
syscall.Sync()
|
||||
|
||||
// Инициируем мягкую перезагрузку через команду shutdown
|
||||
fmt.Println("Initiating soft reboot via shutdown...")
|
||||
|
||||
// Запускаем shutdown в отдельной горутине с небольшой задержкой,
|
||||
// чтобы ответ гарантированно ушёл клиенту до завершения сервера
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond) // ждём 0.5 секунды
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
cmd := exec.Command("shutdown", "-r", "now")
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Printf("Reboot failed: %v\n", err)
|
||||
|
||||
@@ -3,8 +3,8 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -15,44 +15,38 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) {
|
||||
// Thermal zones 0–3
|
||||
for i := 0; i < 4 && !success; i++ {
|
||||
tempPath := fmt.Sprintf("/sys/class/thermal/thermal_zone%d/temp", i)
|
||||
data, err := ioutil.ReadFile(tempPath)
|
||||
data, err := os.ReadFile(tempPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Читаем тип зоны, если доступен
|
||||
typePath := fmt.Sprintf("/sys/class/thermal/thermal_zone%d/type", i)
|
||||
typeData, _ := ioutil.ReadFile(typePath) // ошибку игнорируем
|
||||
typeData, _ := os.ReadFile(typePath)
|
||||
zoneType := strings.TrimSpace(string(typeData))
|
||||
|
||||
// Парсим температуру
|
||||
var tempRaw int
|
||||
if _, err := fmt.Sscanf(string(data), "%d", &tempRaw); err != nil {
|
||||
continue
|
||||
}
|
||||
temp := float64(tempRaw) / 1000.0
|
||||
|
||||
// Проверяем, является ли зона CPU/SOC
|
||||
isCPU := strings.Contains(strings.ToLower(zoneType), "cpu") ||
|
||||
strings.Contains(strings.ToLower(zoneType), "soc")
|
||||
|
||||
temperature = temp
|
||||
success = true
|
||||
if isCPU {
|
||||
break // предпочитаем CPU
|
||||
break
|
||||
}
|
||||
// иначе продолжаем искать CPU
|
||||
}
|
||||
|
||||
// Если thermal не дал результата, пробуем hwmon 0–3
|
||||
for i := 0; i < 4 && !success; i++ {
|
||||
tempPath := fmt.Sprintf("/sys/class/hwmon/hwmon%d/temp1_input", i)
|
||||
data, err := ioutil.ReadFile(tempPath)
|
||||
data, err := os.ReadFile(tempPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Читаем имя сенсора
|
||||
namePath := fmt.Sprintf("/sys/class/hwmon/hwmon%d/name", i)
|
||||
nameData, _ := ioutil.ReadFile(namePath)
|
||||
nameData, _ := os.ReadFile(namePath)
|
||||
sensorName := strings.TrimSpace(string(nameData))
|
||||
|
||||
var tempRaw int
|
||||
@@ -72,7 +66,6 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Финальный fallback – просто temp1_input для первых 4 hwmon
|
||||
if !success {
|
||||
fallbackPaths := []string{
|
||||
"/sys/class/hwmon/hwmon0/temp1_input",
|
||||
@@ -81,7 +74,7 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) {
|
||||
"/sys/class/hwmon/hwmon3/temp1_input",
|
||||
}
|
||||
for _, path := range fallbackPaths {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -95,7 +88,6 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Формируем JSON-ответ
|
||||
response := map[string]interface{}{
|
||||
"success": success,
|
||||
"temperature": temperature,
|
||||
|
||||
+4
-20
@@ -14,7 +14,6 @@ import (
|
||||
"up/utils"
|
||||
)
|
||||
|
||||
// Глобальное состояние загрузки (аналог g_upload в C)
|
||||
type UploadState struct {
|
||||
mu sync.RWMutex
|
||||
Type string
|
||||
@@ -25,7 +24,7 @@ type UploadState struct {
|
||||
|
||||
var uploadState UploadState
|
||||
|
||||
// handleUpload обрабатывает POST /api/upload
|
||||
// POST /api/upload
|
||||
func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
uboot_max_size_int, err := strconv.Atoi(os.Getenv("UBOOT_MAX_SIZE"))
|
||||
if err != nil {
|
||||
@@ -40,15 +39,13 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
UBOOT_MAX_SIZE := uboot_max_size_int * 1024 * 1024
|
||||
MAX_FILE_SIZE := max_file_size_int * 1024 * 1024
|
||||
MOUNT_POINT := os.Getenv("MOUNT_POINT")
|
||||
// Ограничиваем размер запроса (максимум UBOOT_MAX_SIZE + запас)
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(MAX_FILE_SIZE)+1024)
|
||||
if err := r.ParseMultipartForm(32 << 20); err != nil { // 32 MB в памяти
|
||||
http.Error(w, `{"error":"Failed to parse multipart form"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer r.MultipartForm.RemoveAll() // очищаем временные файлы
|
||||
defer r.MultipartForm.RemoveAll()
|
||||
|
||||
// Получаем поле "type"
|
||||
typeVals := r.MultipartForm.Value["type"]
|
||||
if len(typeVals) == 0 {
|
||||
http.Error(w, `{"error":"Missing type field"}`, http.StatusBadRequest)
|
||||
@@ -56,7 +53,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
typ := typeVals[0]
|
||||
|
||||
// Получаем файл из поля "file"
|
||||
files := r.MultipartForm.File["file"]
|
||||
if len(files) == 0 {
|
||||
http.Error(w, `{"error":"Missing file field"}`, http.StatusBadRequest)
|
||||
@@ -64,7 +60,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
fileHeader := files[0]
|
||||
|
||||
// Проверка размера файла
|
||||
maxSize := int64(MAX_FILE_SIZE)
|
||||
if typ == "uboot_bin" {
|
||||
maxSize = int64(UBOOT_MAX_SIZE)
|
||||
@@ -74,7 +69,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Открываем загруженный файл
|
||||
srcFile, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
http.Error(w, `{"error":"Failed to open uploaded file"}`, http.StatusInternalServerError)
|
||||
@@ -82,13 +76,11 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Монтируем устройство
|
||||
if utils.MountDevice() < 0 {
|
||||
http.Error(w, `{"error":"Failed to mount device"}`, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Определяем имя файла назначения
|
||||
filename := utils.GetTargetFilename(typ)
|
||||
if filename == "" {
|
||||
http.Error(w, `{"error":"Invalid upload type"}`, http.StatusBadRequest)
|
||||
@@ -96,7 +88,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
destPath := filepath.Join(MOUNT_POINT, filename)
|
||||
|
||||
// Обновляем состояние загрузки
|
||||
uploadState.mu.Lock()
|
||||
uploadState.Type = typ
|
||||
uploadState.Received = 0
|
||||
@@ -106,7 +97,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fmt.Printf("Upload: %s (%d bytes) -> %s\n", typ, fileHeader.Size, destPath)
|
||||
|
||||
// Получаем timestamp из query и декодируем
|
||||
timestamp := r.URL.Query().Get("timestamp")
|
||||
if timestamp != "" {
|
||||
decoded, err := url.QueryUnescape(timestamp)
|
||||
@@ -115,7 +105,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Специальная обработка для U-Boot
|
||||
if typ == "uboot_bin" {
|
||||
data, err := io.ReadAll(srcFile)
|
||||
if err != nil {
|
||||
@@ -139,7 +128,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
uploadState.Status = "Complete"
|
||||
uploadState.mu.Unlock()
|
||||
} else {
|
||||
// Обычная запись файла чанками с обновлением прогресса
|
||||
destFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
uploadState.mu.Lock()
|
||||
@@ -150,7 +138,7 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
buf := make([]byte, 65536) // 64 KB чанки
|
||||
buf := make([]byte, 65536) // 64 KB chunks
|
||||
var written int64
|
||||
for {
|
||||
n, err := srcFile.Read(buf)
|
||||
@@ -163,7 +151,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
written += int64(n)
|
||||
// Обновляем прогресс
|
||||
uploadState.mu.Lock()
|
||||
uploadState.Received = written
|
||||
uploadState.Progress = int(written * 100 / fileHeader.Size)
|
||||
@@ -181,19 +168,17 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
destFile.Sync()
|
||||
syscall.Sync() // глобальная синхронизация
|
||||
syscall.Sync()
|
||||
uploadState.mu.Lock()
|
||||
uploadState.Progress = 100
|
||||
uploadState.Status = "Complete"
|
||||
uploadState.mu.Unlock()
|
||||
}
|
||||
|
||||
// Запись файла версии с timestamp
|
||||
if timestamp != "" {
|
||||
versionPath := filepath.Join(MOUNT_POINT, filename+".version")
|
||||
vf, err := os.Create(versionPath)
|
||||
if err == nil {
|
||||
// Форматируем ISO 8601 в читаемый вид (как в C)
|
||||
var readableDate string
|
||||
if len(timestamp) >= 19 {
|
||||
readableDate = timestamp[:10] + " " + timestamp[11:19] // "YYYY-MM-DD HH:MM:SS"
|
||||
@@ -208,7 +193,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fmt.Printf("Upload complete: %s\n", destPath)
|
||||
|
||||
// Формируем JSON-ответ
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
|
||||
+2
-7
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -13,7 +14,6 @@ func MountStatus(w http.ResponseWriter, req *http.Request) {
|
||||
var DEVICE = os.Getenv("DEVICE")
|
||||
mounted := false
|
||||
|
||||
// Проверяем, смонтировано ли устройство, читая /proc/mounts
|
||||
f, err := os.Open("/proc/mounts")
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
@@ -25,10 +25,8 @@ func MountStatus(w http.ResponseWriter, req *http.Request) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Ошибки сканера игнорируем (аналогично C‑версии)
|
||||
}
|
||||
|
||||
// Формируем JSON‑ответ
|
||||
response := map[string]interface{}{
|
||||
"mounted": mounted,
|
||||
"mount_point": MOUNT_POINT,
|
||||
@@ -36,10 +34,7 @@ func MountStatus(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
// json.NewEncoder автоматически добавляет символ новой строки в конце,
|
||||
// что соответствует формату "%s\n" в C.
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
// В случае ошибки кодирования уже ничего не сделать,
|
||||
// можно только залогировать (здесь опущено для краткости)
|
||||
fmt.Errorf("Encoding error: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user