refactor(pr): Fixed some PR issues
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -27,7 +29,7 @@ type Admin struct {
|
||||
// cfg provides PostgreSQL connection parameters and the default admin credentials used to create the
|
||||
// default admin user when missing.
|
||||
func Init(cfg *config.Config) {
|
||||
password := url.QueryEscape(cfg.PGPassword)
|
||||
password := url.PathEscape(cfg.PGPassword)
|
||||
|
||||
psqlInfo := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=require",
|
||||
cfg.PGUser, password, cfg.PGHost, cfg.PGPort, cfg.PGDatabase,
|
||||
@@ -45,7 +47,9 @@ func Init(cfg *config.Config) {
|
||||
db.SetMaxOpenConns(25)
|
||||
db.SetMaxIdleConns(5)
|
||||
|
||||
if err = db.Ping(); err != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel
|
||||
if err = db.PingContext(ctx); err != nil {
|
||||
log.Fatalf("Failed to ping database: %v", err)
|
||||
}
|
||||
|
||||
@@ -58,7 +62,9 @@ func Init(cfg *config.Config) {
|
||||
// It is safe to call multiple times; if no connection exists, the call is a no-op.
|
||||
func Close() {
|
||||
if db != nil {
|
||||
db.Close()
|
||||
if err := db.Close(); err != nil {
|
||||
log.Printf("Error closing database connection: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package email
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/rideaware/admin-panel/internal/config"
|
||||
@@ -23,22 +24,24 @@ func SendUpdate(subject, body string) (string, error) {
|
||||
if err != nil {
|
||||
return "Failed to retrieve subscribers", err
|
||||
}
|
||||
|
||||
if len(subscribers) == 0 {
|
||||
return "No subscribers found.", nil
|
||||
}
|
||||
|
||||
var succeeded, failed int
|
||||
for _, email := range subscribers {
|
||||
if !send(subject, body, email) {
|
||||
return fmt.Sprintf("Failed to send to %s", email), nil
|
||||
if send(subject, body, email) {
|
||||
succeeded++
|
||||
} else {
|
||||
failed++
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.LogNewsletter(subject, body); err != nil {
|
||||
log.Printf("Error logging newsletter: %v", err)
|
||||
}
|
||||
|
||||
return "Email has been sent to all subscribers.", nil
|
||||
if failed == 0 {
|
||||
return fmt.Sprintf("Email sent to all %d subscribers.", succeeded), nil
|
||||
}
|
||||
return fmt.Sprintf("Sent to %d/%d subscribers; %d failed.", succeeded, succeeded+failed, failed), nil
|
||||
}
|
||||
|
||||
// send constructs and sends an HTML newsletter update to the specified recipient using the current SMTP configuration.
|
||||
@@ -72,7 +75,7 @@ func send(subject, body, recipient string) bool {
|
||||
m.Subject(subject)
|
||||
|
||||
unsubLink := fmt.Sprintf("https://%s/unsubscribe?email=%s",
|
||||
cfg.BaseURL, recipient)
|
||||
cfg.BaseURL, url.QueryEscape(recipient))
|
||||
|
||||
htmlBody := fmt.Sprintf(
|
||||
"%s<br><br>If you ever wish to unsubscribe, "+
|
||||
|
||||
@@ -21,9 +21,16 @@ func SendUpdatePost(c *gin.Context) {
|
||||
subject := c.PostForm("subject")
|
||||
body := c.PostForm("body")
|
||||
|
||||
// validate inputs
|
||||
if strings.TrimSpace(subject) == "" || strings.TrimSpace(body) == {
|
||||
c.HTML(http,StatusBadRequest, "send_update.html",
|
||||
gin.H{"error": "Subject and message cannot be empty"})
|
||||
return
|
||||
}
|
||||
|
||||
message, err := email.SendUpdate(subject, body)
|
||||
if err != nil {
|
||||
c.HTML(http.StatusOK, "send_update.html",
|
||||
c.HTML(http.StatusInternalServerError, "send_update.html",
|
||||
gin.H{"error": message})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
func IndexGet(c *gin.Context) {
|
||||
emails, err := database.GetAllEmails()
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
c.HTML(http.StatusInternalServerError, "admin_index.html",
|
||||
gin.H{"error": "Failed to retrieve subscribers"})
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "admin_index.html",
|
||||
|
||||
@@ -15,6 +15,10 @@ var store *sessions.CookieStore
|
||||
// It panics if config.Current.SecretKey is empty.
|
||||
// The created store is configured with Path "/", MaxAge one week, HttpOnly true, Secure false, and SameSite 0.
|
||||
func Init() {
|
||||
if config.Current == nil {
|
||||
panic("config was not loaded; call config.Load() before middleware.Init()")
|
||||
}
|
||||
|
||||
if config.Current.SecretKey == "" {
|
||||
panic("SECRET_KEY not set")
|
||||
}
|
||||
@@ -23,8 +27,8 @@ func Init() {
|
||||
Path: "/",
|
||||
MaxAge: 86400 * 7,
|
||||
HttpOnly: true,
|
||||
Secure: false,
|
||||
SameSite: 0,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +44,11 @@ func GetStore() *sessions.CookieStore {
|
||||
// Otherwise the middleware calls the next handler in the chain.
|
||||
func Auth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if store == nil {
|
||||
c.String(http.StatusInternalServerError, "Session store not initialized.")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
session, err := store.Get(c.Request, "session")
|
||||
if err != nil || session.Values["username"] == nil {
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
|
||||
Reference in New Issue
Block a user