Files

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")
}
}