Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/tphakala/birdnet-go into sp…
Browse files Browse the repository at this point in the history
…ecies-config-ui
  • Loading branch information
tphakala committed Jan 9, 2025
2 parents f0abd4e + 33c4007 commit e3721d2
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 43 deletions.
1 change: 1 addition & 0 deletions internal/conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ type AllowCloudflareBypass struct {
// SecurityConfig handles all security-related settings and validations
// for the application, including authentication, TLS, and access control.
type Security struct {
Debug bool // true to enable debug mode

// Host is the primary hostname used for TLS certificates
// and OAuth redirect URLs. Required when using AutoTLS or
Expand Down
7 changes: 7 additions & 0 deletions internal/security/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import (
"testing"
"time"

"os"

"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/markbates/goth/gothic"
"github.com/tphakala/birdnet-go/internal/conf"
)

Expand Down Expand Up @@ -166,6 +170,9 @@ func TestHandleBasicAuthTokenSuccess(t *testing.T) {
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)

// Initialize Gothic session
gothic.Store = sessions.NewFilesystemStore(os.TempDir(), []byte("secret-key"))

s := &OAuth2Server{
Settings: &conf.Settings{
Security: conf.Security{
Expand Down
7 changes: 5 additions & 2 deletions internal/security/cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type CloudflareAccess struct {

func NewCloudflareAccess() *CloudflareAccess {
settings := conf.GetSettings()
debug := settings.Security.Debug
cfBypass := settings.Security.AllowCloudflareBypass

return &CloudflareAccess{
Expand All @@ -55,6 +56,7 @@ func NewCloudflareAccess() *CloudflareAccess {
lastFetch: time.Time{},
},
settings: &cfBypass,
debug: debug,
}
}

Expand Down Expand Up @@ -279,10 +281,11 @@ func (ca *CloudflareAccess) GetLogoutURL() string {

func (ca *CloudflareAccess) Debug(format string, v ...interface{}) {
if !ca.debug {
prefix := "[security/cloudflare] "
if len(v) == 0 {
log.Print(format)
log.Print(prefix + format)
} else {
log.Printf(format, v...)
log.Printf(prefix+format, v...)
}
}
}
6 changes: 3 additions & 3 deletions internal/security/cloudflare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,9 @@ func TestFetchCertsLogging(t *testing.T) {
}

expectedLogs := []string{
fmt.Sprintf("Fetching Cloudflare certs from URL: %s/cdn-cgi/access/certs", server.URL),
"Added certificate with Kid: 1234",
"Added certificate with Kid: 5678",
fmt.Sprintf("[security/cloudflare] Fetching Cloudflare certs from URL: %s/cdn-cgi/access/certs", server.URL),
"[security/cloudflare] Added certificate with Kid: 1234",
"[security/cloudflare] Added certificate with Kid: 5678",
}

for i, logMsg := range logs {
Expand Down
7 changes: 4 additions & 3 deletions internal/security/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type OAuth2Server struct {

func NewOAuth2Server() *OAuth2Server {
settings := conf.GetSettings()
debug := settings.Debug
debug := settings.Security.Debug

server := &OAuth2Server{
Settings: settings,
Expand Down Expand Up @@ -256,10 +256,11 @@ func (s *OAuth2Server) StartAuthCleanup(interval time.Duration) {

func (s *OAuth2Server) Debug(format string, v ...interface{}) {
if s.debug {
prefix := "[security/oauth] "
if len(v) == 0 {
log.Print(format)
log.Print(prefix + format)
} else {
log.Printf(format, v...)
log.Printf(prefix+format, v...)
}
}
}
91 changes: 56 additions & 35 deletions views/settings/settingsBase.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
this.eventSource = new EventSource('/sse');
this.eventSource.onmessage = (event) => {
const notification = JSON.parse(event.data);

this.notificationCallbacks.forEach(callback => callback(notification));
};

Expand Down Expand Up @@ -105,28 +106,42 @@
notifications: [],
hasChanges: false,
saving: false,
handleNotification: null,
initialized: false,
init() {
// Subscribe to notifications using the singleton SSE manager
const handleNotification = (notification) => {
this.addNotification(notification.message, notification.type);
if (this.initialized) {
return;
}
this.handleNotification = (notification) => {
const id = Date.now() + Math.random();
this.notifications.push({
id: id,
message: notification.message,
type: notification.type,
removing: false
});
setTimeout(() => {
const index = this.notifications.findIndex(n => n.id === id);
if (index !== -1) {
this.notifications[index].removing = true;
setTimeout(() => {
this.notifications = this.notifications.filter(n => n.id !== id);
}, 300);
}
}, 5000);
};
window.SSEManager.subscribe(handleNotification);
window.SSEManager.init(); // Initialize if not already done
window.SSEManager.subscribe(this.handleNotification);
window.SSEManager.init();
// Cleanup on component destroy
this.$cleanup(() => {
window.SSEManager.unsubscribe(handleNotification);
this.$el.addEventListener('alpine:destroyed', () => {
window.SSEManager.unsubscribe(this.handleNotification);
});
},
addNotification(message, type) {
const id = Date.now();
this.notifications.push({ id, message, type });
setTimeout(() => {
this.notifications = this.notifications.filter(n => n.id !== id);
}, 5000);
this.initialized = true;
},
isFormValid(form) {
const inputSelector = 'input[type=\'password\'][required], input[type=\'text\'][required]';
Expand Down Expand Up @@ -168,8 +183,6 @@
const security = Alpine.store('security');
if (security?.hasChanges) {
setTimeout(() => {
this.notifications = []; // Clear existing notifications
this.addNotification('Settings saved, reloading page...', 'success');
setTimeout(() => window.location.reload(), 1500);
}, 50);
} else {
Expand Down Expand Up @@ -203,27 +216,35 @@

<div class="fixed bottom-5 right-5 z-50 flex flex-col space-y-2">
<template x-for="notification in notifications" :key="notification.id">
<div x-show="!notification.removing" :class="{
<div x-show="!notification.removing"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform translate-x-8"
x-transition:enter-end="opacity-100 transform translate-x-0"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 transform translate-x-0"
x-transition:leave-end="opacity-0 transform translate-x-8"
:class="{
'alert-success': notification.type === 'success',
'alert-error': notification.type === 'error',
'alert-info': notification.type === 'info'
}" class="alert">
}"
class="alert shadow-lg">
<div class="flex items-start">
<svg x-show="notification.type === 'success'" xmlns="http://www.w3.org/2000/svg"
class="stroke-current flex-shrink-0 h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<svg x-show="notification.type === 'error'" xmlns="http://www.w3.org/2000/svg"
class="stroke-current flex-shrink-0 h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<svg x-show="notification.type === 'info'" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" class="stroke-current flex-shrink-0 w-6 h-6 mr-2">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<template x-if="notification.type === 'success'">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
<template x-if="notification.type === 'error'">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
<template x-if="notification.type === 'info'">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
<span x-text="notification.message"></span>
</div>
</div>
Expand Down

0 comments on commit e3721d2

Please sign in to comment.