diff --git a/backend/internal/api/handlers/logs_handler.go b/backend/internal/api/handlers/logs_handler.go index d23dcc5a..d9ca204c 100644 --- a/backend/internal/api/handlers/logs_handler.go +++ b/backend/internal/api/handlers/logs_handler.go @@ -39,6 +39,7 @@ func (h *LogsHandler) Read(c *gin.Context) { Search: c.Query("search"), Host: c.Query("host"), Status: c.Query("status"), + Level: c.Query("level"), Limit: limit, Offset: offset, } diff --git a/backend/internal/caddy/config.go b/backend/internal/caddy/config.go index 6c9e2d0f..28c7d12c 100644 --- a/backend/internal/caddy/config.go +++ b/backend/internal/caddy/config.go @@ -23,7 +23,7 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir string, acmeEmail strin Logging: &LoggingConfig{ Logs: map[string]*LogConfig{ "access": { - Level: "INFO", + Level: "DEBUG", Writer: &WriterConfig{ Output: "file", Filename: logFile, diff --git a/backend/internal/caddy/config_test.go b/backend/internal/caddy/config_test.go index 5600db94..97e19a47 100644 --- a/backend/internal/caddy/config_test.go +++ b/backend/internal/caddy/config_test.go @@ -119,18 +119,15 @@ func TestGenerateConfig_Logging(t *testing.T) { config, err := GenerateConfig(hosts, "/tmp/caddy-data", "admin@example.com") require.NoError(t, err) - // Verify logging config + // Verify logging configuration require.NotNil(t, config.Logging) require.NotNil(t, config.Logging.Logs) - require.Contains(t, config.Logging.Logs, "access") - - logConfig := config.Logging.Logs["access"] - require.Equal(t, "INFO", logConfig.Level) - require.NotNil(t, logConfig.Writer) - require.Equal(t, "file", logConfig.Writer.Output) - require.Contains(t, logConfig.Writer.Filename, "access.log") - require.NotNil(t, logConfig.Writer.RollSize) - require.NotNil(t, logConfig.Writer.RollKeep) + require.NotNil(t, config.Logging.Logs["access"]) + require.Equal(t, "DEBUG", config.Logging.Logs["access"].Level) + require.Contains(t, config.Logging.Logs["access"].Writer.Filename, "access.log") + require.Equal(t, 10, config.Logging.Logs["access"].Writer.RollSize) + require.Equal(t, 5, config.Logging.Logs["access"].Writer.RollKeep) + require.Equal(t, 7, config.Logging.Logs["access"].Writer.RollKeepDays) } func TestGenerateConfig_Advanced(t *testing.T) { diff --git a/backend/internal/models/log_entry.go b/backend/internal/models/log_entry.go index 52e95592..465b776d 100644 --- a/backend/internal/models/log_entry.go +++ b/backend/internal/models/log_entry.go @@ -36,6 +36,7 @@ type LogFilter struct { Search string `form:"search"` Host string `form:"host"` Status string `form:"status"` // e.g., "200", "4xx", "5xx" + Level string `form:"level"` Limit int `form:"limit"` Offset int `form:"offset"` } diff --git a/backend/internal/services/log_service.go b/backend/internal/services/log_service.go index 6c9563c6..cdb155ea 100644 --- a/backend/internal/services/log_service.go +++ b/backend/internal/services/log_service.go @@ -175,6 +175,13 @@ func (s *LogService) matchesFilter(entry models.CaddyAccessLog, filter models.Lo } } + // Level Filter + if filter.Level != "" { + if !strings.EqualFold(entry.Level, filter.Level) { + return false + } + } + // Host Filter if filter.Host != "" { if !strings.Contains(strings.ToLower(entry.Request.Host), strings.ToLower(filter.Host)) { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index abb72698..7e7ff45f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,8 +9,6 @@ import ProxyHosts from './pages/ProxyHosts' import RemoteServers from './pages/RemoteServers' import ImportCaddy from './pages/ImportCaddy' import Certificates from './pages/Certificates' -import SettingsLayout from './pages/SettingsLayout' -import TasksLayout from './pages/TasksLayout' import SystemSettings from './pages/SystemSettings' import Account from './pages/Account' import Backups from './pages/Backups' @@ -43,14 +41,14 @@ export default function App() { } /> {/* Settings Routes */} - }> + } /> } /> } /> {/* Tasks Routes */} - }> + } /> } /> } /> diff --git a/frontend/src/api/logs.ts b/frontend/src/api/logs.ts index 1fb9bda0..8e42af9f 100644 --- a/frontend/src/api/logs.ts +++ b/frontend/src/api/logs.ts @@ -35,6 +35,7 @@ export interface LogFilter { search?: string; host?: string; status?: string; + level?: string; limit?: number; offset?: number; } @@ -49,6 +50,7 @@ export const getLogContent = async (filename: string, filter: LogFilter = {}): P if (filter.search) params.append('search', filter.search); if (filter.host) params.append('host', filter.host); if (filter.status) params.append('status', filter.status); + if (filter.level) params.append('level', filter.level); if (filter.limit) params.append('limit', filter.limit.toString()); if (filter.offset) params.append('offset', filter.offset.toString()); diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 1c97c9d7..4ecaa19c 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -39,12 +39,26 @@ export default function Layout({ children }: LayoutProps) { { name: 'Domains', path: '/domains', icon: '🌍' }, { name: 'Certificates', path: '/certificates', icon: '🔒' }, { name: 'Import Caddyfile', path: '/import', icon: '📥' }, - { name: 'Settings', path: '/settings/system', icon: '⚙️' }, - { name: 'Tasks', path: '/tasks/backups', icon: '📋' }, + { + name: 'Settings', + icon: '⚙️', + children: [ + { name: 'System', path: '/settings/system', icon: '⚙️' }, + { name: 'Account', path: '/settings/account', icon: '🛡️' }, + ] + }, + { + name: 'Tasks', + icon: '📋', + children: [ + { name: 'Backups', path: '/tasks/backups', icon: '💾' }, + { name: 'Logs', path: '/tasks/logs', icon: '📝' }, + ] + }, ] return ( -
+
{/* Mobile Header */}

CPM+

@@ -71,14 +85,49 @@ export default function Layout({ children }: LayoutProps) {