310 lines
9.2 KiB
Go
Executable File
310 lines
9.2 KiB
Go
Executable File
package main
|
|
|
|
import (
|
|
"embed"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"audio-recorder/database"
|
|
"audio-recorder/devices"
|
|
"audio-recorder/logger"
|
|
"audio-recorder/optimization"
|
|
"audio-recorder/recorder"
|
|
"audio-recorder/server"
|
|
"audio-recorder/smb"
|
|
"audio-recorder/vpn"
|
|
)
|
|
|
|
//go:embed static
|
|
var staticFiles embed.FS
|
|
|
|
func main() {
|
|
// Оптимизация для архитектуры
|
|
optimization.OptimizeGoroutines()
|
|
|
|
// Инициализация логгера
|
|
logger.Init()
|
|
|
|
// Инициализация базы данных
|
|
db := database.GetDB()
|
|
defer db.Close()
|
|
|
|
// Инициализация настроек по умолчанию
|
|
if err := db.InitDefaultSettings(); err != nil {
|
|
log.Printf("Warning: Failed to init default settings: %v", err)
|
|
}
|
|
|
|
// Загружаем пользователей из БД
|
|
loadUsersFromDB()
|
|
|
|
// Получаем папку для записей из БД
|
|
recordingsDir, err := db.GetSetting("recordings_dir")
|
|
if err != nil || recordingsDir == "" {
|
|
recordingsDir = "recordings"
|
|
if err := db.SetSetting("recordings_dir", recordingsDir); err != nil {
|
|
log.Printf("Warning: Failed to save recordings_dir to DB: %v", err)
|
|
}
|
|
}
|
|
|
|
// Создаем папку для записей
|
|
if err := os.MkdirAll(recordingsDir, 0755); err != nil {
|
|
log.Fatal("Не удалось создать папку для записей:", err)
|
|
}
|
|
|
|
log.Printf("Папка для записей: %s", recordingsDir)
|
|
|
|
// Инициализируем устройства при старте
|
|
dm := devices.GetManager()
|
|
log.Printf("Сканирование устройств при старте...")
|
|
devicesList := dm.ScanDevices()
|
|
log.Printf("Найдено устройств: %d", len(devicesList))
|
|
|
|
// Сохраняем устройства в БД
|
|
for _, dev := range devicesList {
|
|
status := "✓"
|
|
if !dev.Working {
|
|
status = "✗"
|
|
}
|
|
log.Printf(" %s %s: %s", status, dev.Name, dev.Description)
|
|
|
|
dbDevice := database.DeviceDB{
|
|
Name: dev.Name,
|
|
Card: dev.Card,
|
|
Device: dev.Device,
|
|
Description: dev.Description,
|
|
Channels: dev.Channels,
|
|
Working: dev.Working,
|
|
LastTest: time.Now(),
|
|
}
|
|
if err := db.SaveDevice(dbDevice); err != nil {
|
|
log.Printf("Warning: Failed to save device to DB: %v", err)
|
|
}
|
|
}
|
|
|
|
// Создаем рекордер
|
|
rec := recorder.NewRecorder(recordingsDir)
|
|
|
|
// Проверяем, есть ли каналы в БД
|
|
channels, err := db.GetAllChannels()
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to get channels from DB: %v", err)
|
|
}
|
|
|
|
if len(channels) == 0 {
|
|
log.Println("No channels in database, creating default channels...")
|
|
|
|
os.MkdirAll(filepath.Join(recordingsDir, "channel1"), 0755)
|
|
os.MkdirAll(filepath.Join(recordingsDir, "channel2"), 0755)
|
|
|
|
channel1 := recorder.NewChannel(
|
|
"channel1", "Канал 1", "default",
|
|
60, 8000, 1, "economy", false, recordingsDir,
|
|
)
|
|
channel1.SMBFolder = "channel1"
|
|
rec.AddChannel(channel1)
|
|
|
|
channel2 := recorder.NewChannel(
|
|
"channel2", "Канал 2", "default",
|
|
60, 8000, 1, "economy", false, recordingsDir,
|
|
)
|
|
channel2.SMBFolder = "channel2"
|
|
rec.AddChannel(channel2)
|
|
|
|
log.Println("Default channels created successfully")
|
|
}
|
|
|
|
server.SetRecorder(rec)
|
|
recorder.SetRecorderInstance(rec)
|
|
recorder.SetDeviceManager(dm)
|
|
|
|
restoreChannelsState(rec, dm, db)
|
|
|
|
stateChecker := recorder.GetStateChecker()
|
|
stateChecker.Start()
|
|
|
|
// Настройка SMB клиента
|
|
smbClient := smb.GetSMBClient()
|
|
|
|
// Загружаем настройки SMB из БД
|
|
smbEnabledStr, _ := db.GetSetting("smb_enabled")
|
|
smbAutoStartStr, _ := db.GetSetting("smb_auto_start")
|
|
smbServer, _ := db.GetSetting("smb_server")
|
|
smbPortStr, _ := db.GetSetting("smb_port")
|
|
smbShare, _ := db.GetSetting("smb_share")
|
|
smbWorkgroup, _ := db.GetSetting("smb_workgroup")
|
|
smbUsername, _ := db.GetSetting("smb_username")
|
|
smbPassword, _ := db.GetSetting("smb_password")
|
|
smbBasePath, _ := db.GetSetting("smb_base_path")
|
|
|
|
smbPort := 445
|
|
if smbPortStr != "" {
|
|
if port, err := strconv.Atoi(smbPortStr); err == nil {
|
|
smbPort = port
|
|
}
|
|
}
|
|
|
|
smbConfig := smb.SMBConfig{
|
|
Enabled: smbEnabledStr == "true",
|
|
AutoStart: smbAutoStartStr == "true",
|
|
Server: smbServer,
|
|
Port: smbPort,
|
|
Share: smbShare,
|
|
Workgroup: smbWorkgroup,
|
|
Username: smbUsername,
|
|
Password: smbPassword,
|
|
BasePath: smbBasePath,
|
|
}
|
|
|
|
if err := smbClient.UpdateConfig(smbConfig); err != nil {
|
|
log.Printf("Warning: Failed to update SMB config: %v", err)
|
|
}
|
|
|
|
recorder.SetRecordingCompletedCallback(func(filename string, filePath string, smbFolder string) {
|
|
if smbClient.IsConnected() {
|
|
log.Printf("SMB client connected, uploading...")
|
|
if err := smbClient.UploadFile(filePath, smbFolder, filename); err != nil {
|
|
log.Printf("❌ Не удалось загрузить файл на SMB: %v", err)
|
|
smbClient.AddToRetry(filePath, smbFolder, filename)
|
|
} else {
|
|
log.Printf("✅ Файл успешно загружен на SMB: %s", filename)
|
|
if err := os.Remove(filePath); err != nil {
|
|
log.Printf("Не удалось удалить локальный файл: %v", err)
|
|
} else {
|
|
log.Printf("Локальный файл удален: %s", filePath)
|
|
}
|
|
}
|
|
} else {
|
|
log.Printf("⚠️ SMB клиент не подключен")
|
|
smbClient.AddToRetry(filePath, smbFolder, filename)
|
|
}
|
|
})
|
|
|
|
if smbEnabledStr == "true" {
|
|
log.Printf("Запуск SMB клиента...")
|
|
if err := smbClient.Start(); err != nil {
|
|
log.Printf("Предупреждение: Не удалось запустить SMB клиент: %v", err)
|
|
}
|
|
}
|
|
|
|
go smbClient.ProcessRetryQueue()
|
|
|
|
// Инициализация VPN
|
|
openvpn := vpn.GetOpenVPNClient()
|
|
openvpn.RestoreState()
|
|
|
|
wireguard := vpn.GetWireGuardClient()
|
|
wireguard.LoadFromDB()
|
|
|
|
vpnManager := vpn.GetVPNManager()
|
|
vpnManager.Init()
|
|
|
|
// Настраиваем обработку сигналов
|
|
signalChan := make(chan os.Signal, 1)
|
|
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
|
|
|
go func() {
|
|
<-signalChan
|
|
log.Println("Получен сигнал завершения, сохраняем состояния каналов...")
|
|
stateChecker.Stop()
|
|
smbClient.Stop()
|
|
openvpn.Stop()
|
|
wireguard.Stop()
|
|
vpnManager.StopAll()
|
|
rec.SaveAllStates()
|
|
log.Println("Завершаем работу...")
|
|
os.Exit(0)
|
|
}()
|
|
|
|
// Настраиваем и запускаем HTTP сервер
|
|
srv := server.NewServer(staticFiles, recordingsDir)
|
|
srv.SetupRoutes()
|
|
|
|
log.Printf("")
|
|
log.Printf("🌐 HTTP сервер запущен на http://localhost:80")
|
|
log.Printf("📝 Логин: admin, Пароль: admin")
|
|
log.Printf("📁 Папка записей: %s", recordingsDir)
|
|
log.Printf("🔄 Проверка состояния каналов: каждые %d секунд", stateChecker.GetCheckInterval())
|
|
log.Printf("🏗️ Архитектура: %s", optimization.GetArch())
|
|
log.Printf("")
|
|
|
|
if err := srv.Run(); err != nil {
|
|
log.Fatal("Server error:", err)
|
|
}
|
|
}
|
|
|
|
func restoreChannelsState(rec *recorder.Recorder, dm *devices.DeviceManager, db *database.DB) {
|
|
channels := rec.GetAllChannels()
|
|
log.Printf("=== Восстановление состояния каналов из БД ===")
|
|
|
|
for id, ch := range channels {
|
|
state, err := db.GetChannelState(id)
|
|
if err != nil {
|
|
log.Printf("Failed to get state for channel %s: %v", id, err)
|
|
continue
|
|
}
|
|
|
|
log.Printf("Канал %s: состояние в БД = %v, текущее состояние = %v", id, state, ch.IsRecording)
|
|
|
|
if state {
|
|
log.Printf("Запись для канала %s должна быть активна. Запускаем...", id)
|
|
|
|
testFunc := func(device string) (bool, string) {
|
|
return dm.TestDevice(device)
|
|
}
|
|
|
|
channelID := id
|
|
channel := ch
|
|
go func() {
|
|
// Для ARM добавляем задержку
|
|
if optimization.IsARM() {
|
|
time.Sleep(3 * time.Second)
|
|
} else {
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
if err := channel.StartRecording(channel.OutputDir, testFunc); err != nil {
|
|
log.Printf("Не удалось восстановить запись для канала %s: %v", channelID, err)
|
|
db.SetChannelState(channelID, false)
|
|
} else {
|
|
log.Printf("✅ Запись успешно восстановлена для канала %s", channelID)
|
|
}
|
|
}()
|
|
} else {
|
|
log.Printf("Запись для канала %s не должна быть активна. Состояние OK.", id)
|
|
}
|
|
}
|
|
log.Printf("=== Восстановление состояний завершено ===")
|
|
}
|
|
|
|
func loadUsersFromDB() {
|
|
db := database.GetDB()
|
|
users, err := db.GetAllUsers()
|
|
if err != nil {
|
|
log.Printf("Error loading users from DB: %v", err)
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
hash, err := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
log.Printf("Error hashing admin password: %v", err)
|
|
return
|
|
}
|
|
adminUser := database.UserDB{
|
|
Username: "admin",
|
|
PasswordHash: string(hash),
|
|
Role: "admin",
|
|
CreatedAt: time.Now(),
|
|
}
|
|
if err := db.SaveUser(adminUser); err != nil {
|
|
log.Printf("Error saving admin user: %v", err)
|
|
}
|
|
log.Printf("Default admin user created with password: admin")
|
|
}
|
|
}
|