Massive refactor!
This commit is contained in:
32
bootstrap/license.go
Normal file
32
bootstrap/license.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
internal "synlotto-website/internal/licensecheck"
|
||||||
|
"synlotto-website/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalChecker *internal.LicenseChecker
|
||||||
|
|
||||||
|
func InitLicenseChecker(config *models.Config) error {
|
||||||
|
checker := &internal.LicenseChecker{
|
||||||
|
LicenseAPIURL: config.License.APIURL,
|
||||||
|
APIKey: config.License.APIKey,
|
||||||
|
PollInterval: 10 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checker.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.StartBackgroundCheck()
|
||||||
|
globalChecker = checker
|
||||||
|
log.Println("✅ License validation started.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLicenseChecker() *internal.LicenseChecker {
|
||||||
|
return globalChecker
|
||||||
|
}
|
||||||
@@ -3,23 +3,27 @@ package handlers
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"synlotto-website/helpers"
|
|
||||||
"synlotto-website/models"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
securityHelpers "license-server/helpers/security"
|
||||||
|
httpHelpers "synlotto-website/helpers/http"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
"synlotto-website/models"
|
||||||
|
"synlotto-website/storage"
|
||||||
|
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Login(w http.ResponseWriter, r *http.Request) {
|
func Login(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
session, _ := helpers.GetSession(w, r)
|
session, _ := httpHelpers.GetSession(w, r)
|
||||||
if _, ok := session.Values["user_id"].(int); ok {
|
if _, ok := session.Values["user_id"].(int); ok {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tmpl := helpers.LoadTemplateFiles("login.html", "templates/account/login.html")
|
tmpl := templateHelpers.LoadTemplateFiles("login.html", "templates/account/login.html")
|
||||||
|
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
context["csrfField"] = csrf.TemplateField(r)
|
context["csrfField"] = csrf.TemplateField(r)
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
@@ -34,12 +38,12 @@ func Login(w http.ResponseWriter, r *http.Request) {
|
|||||||
password := r.FormValue("password")
|
password := r.FormValue("password")
|
||||||
|
|
||||||
user := models.GetUserByUsername(username)
|
user := models.GetUserByUsername(username)
|
||||||
if user == nil || !helpers.CheckPasswordHash(user.PasswordHash, password) {
|
if user == nil || !securityHelpers.CheckPasswordHash(user.PasswordHash, password) {
|
||||||
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session, _ := helpers.GetSession(w, r)
|
session, _ := httpHelpers.GetSession(w, r)
|
||||||
|
|
||||||
for k := range session.Values {
|
for k := range session.Values {
|
||||||
delete(session.Values, k)
|
delete(session.Values, k)
|
||||||
@@ -65,18 +69,18 @@ func Login(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil || !helpers.CheckPasswordHash(user.PasswordHash, password) {
|
if user == nil || !securityHelpers.CheckPasswordHash(user.PasswordHash, password) {
|
||||||
models.LogLoginAttempt(username, false)
|
storage.LogLoginAttempt(username, false)
|
||||||
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
models.LogLoginAttempt(username, true)
|
storage.LogLoginAttempt(username, true)
|
||||||
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Logout(w http.ResponseWriter, r *http.Request) {
|
func Logout(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := helpers.GetSession(w, r)
|
session, _ := httpHelpers.GetSession(w, r)
|
||||||
|
|
||||||
for k := range session.Values {
|
for k := range session.Values {
|
||||||
delete(session.Values, k)
|
delete(session.Values, k)
|
||||||
@@ -95,7 +99,7 @@ func Logout(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func Signup(w http.ResponseWriter, r *http.Request) {
|
func Signup(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
tmpl := helpers.LoadTemplateFiles("signup.html", "templates/account/signup.html")
|
tmpl := templateHelpers.LoadTemplateFiles("signup.html", "templates/account/signup.html")
|
||||||
|
|
||||||
tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
||||||
"csrfField": csrf.TemplateField(r),
|
"csrfField": csrf.TemplateField(r),
|
||||||
@@ -106,7 +110,7 @@ func Signup(w http.ResponseWriter, r *http.Request) {
|
|||||||
username := r.FormValue("username")
|
username := r.FormValue("username")
|
||||||
password := r.FormValue("password")
|
password := r.FormValue("password")
|
||||||
|
|
||||||
hashed, err := helpers.HashPassword(password)
|
hashed, err := securityHelpers.HashPassword(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"synlotto-website/helpers"
|
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/middleware"
|
"synlotto-website/middleware"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
@@ -19,7 +21,7 @@ type AdminLogEntry struct {
|
|||||||
|
|
||||||
func AdminAccessLogHandler(db *sql.DB) http.HandlerFunc {
|
func AdminAccessLogHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return middleware.Auth(true)(func(w http.ResponseWriter, r *http.Request) {
|
return middleware.Auth(true)(func(w http.ResponseWriter, r *http.Request) {
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
|
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
SELECT accessed_at, user_id, path, ip, user_agent
|
SELECT accessed_at, user_id, path, ip, user_agent
|
||||||
@@ -45,7 +47,7 @@ func AdminAccessLogHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
context["AuditLogs"] = logs
|
context["AuditLogs"] = logs
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("access_log.html", "templates/admin/logs/access_log.html")
|
tmpl := templateHelpers.LoadTemplateFiles("access_log.html", "templates/admin/logs/access_log.html")
|
||||||
|
|
||||||
_ = tmpl.ExecuteTemplate(w, "layout", context)
|
_ = tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
})
|
})
|
||||||
@@ -53,7 +55,7 @@ func AdminAccessLogHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
func AuditLogHandler(db *sql.DB) http.HandlerFunc {
|
func AuditLogHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return middleware.Auth(true)(func(w http.ResponseWriter, r *http.Request) {
|
return middleware.Auth(true)(func(w http.ResponseWriter, r *http.Request) {
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
|
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
SELECT timestamp, user_id, action, ip, user_agent
|
SELECT timestamp, user_id, action, ip, user_agent
|
||||||
@@ -81,7 +83,7 @@ func AuditLogHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
context["AuditLogs"] = logs
|
context["AuditLogs"] = logs
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("audit.html", "templates/admin/logs/audit.html")
|
tmpl := templateHelpers.LoadTemplateFiles("audit.html", "templates/admin/logs/audit.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
helpers "synlotto-website/helpers"
|
helpers "synlotto-website/helpers"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AdminDashboardHandler(db *sql.DB) http.HandlerFunc {
|
func AdminDashboardHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// userID, ok := helpers.GetCurrentUserID(r)
|
// userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
// if !ok {
|
// if !ok {
|
||||||
// http.Redirect(w, r, "/login", http.StatusSeeOther)
|
// http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
// return
|
// return
|
||||||
@@ -19,7 +20,7 @@ func AdminDashboardHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
// TODO: check is_admin from users table here
|
// TODO: check is_admin from users table here
|
||||||
|
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
|
|
||||||
// Total ticket stats
|
// Total ticket stats
|
||||||
var total, winners int
|
var total, winners int
|
||||||
@@ -54,7 +55,7 @@ func AdminDashboardHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
context["MatchLogs"] = logs
|
context["MatchLogs"] = logs
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("dashboard.html", "templates/admin/dashboard.html")
|
tmpl := templateHelpers.LoadTemplateFiles("dashboard.html", "templates/admin/dashboard.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
helpers "synlotto-website/helpers"
|
helpers "synlotto-website/helpers"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDrawHandler(db *sql.DB) http.HandlerFunc {
|
func NewDrawHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
|
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
game := r.FormValue("game_type")
|
game := r.FormValue("game_type")
|
||||||
@@ -30,7 +32,7 @@ func NewDrawHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("new_draw", "templates/admin/draws/new_draw.html")
|
tmpl := templateHelpers.LoadTemplateFiles("new_draw", "templates/admin/draws/new_draw.html")
|
||||||
|
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
})
|
})
|
||||||
@@ -72,7 +74,7 @@ func DeleteDrawHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
func ListDrawsHandler(db *sql.DB) http.HandlerFunc {
|
func ListDrawsHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
draws := []models.DrawSummary{}
|
draws := []models.DrawSummary{}
|
||||||
|
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
@@ -100,7 +102,7 @@ func ListDrawsHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
context["Draws"] = draws
|
context["Draws"] = draws
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("list.html", "templates/admin/draws/list.html")
|
tmpl := templateHelpers.LoadTemplateFiles("list.html", "templates/admin/draws/list.html")
|
||||||
|
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
"synlotto-website/models"
|
|
||||||
services "synlotto-website/services/tickets"
|
services "synlotto-website/services/tickets"
|
||||||
|
|
||||||
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AdminTriggersHandler(db *sql.DB) http.HandlerFunc {
|
func AdminTriggersHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
|
|
||||||
if flash := r.URL.Query().Get("flash"); flash != "" {
|
if flash := r.URL.Query().Get("flash"); flash != "" {
|
||||||
context["Flash"] = flash
|
context["Flash"] = flash
|
||||||
@@ -71,7 +72,7 @@ func AdminTriggersHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("triggers.html", "templates/admin/triggers.html")
|
tmpl := templateHelpers.LoadTemplateFiles("triggers.html", "templates/admin/triggers.html")
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"synlotto-website/helpers"
|
helpers "synlotto-website/helpers"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddPrizesHandler(db *sql.DB) http.HandlerFunc {
|
func AddPrizesHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
tmpl := helpers.LoadTemplateFiles("add_prizes.html", "templates/admin/draws/prizes/add_prizes.html")
|
tmpl := templateHelpers.LoadTemplateFiles("add_prizes.html", "templates/admin/draws/prizes/add_prizes.html")
|
||||||
|
|
||||||
tmpl.ExecuteTemplate(w, "layout", helpers.TemplateContext(w, r, models.TemplateData{}))
|
tmpl.ExecuteTemplate(w, "layout", templateHelpers.TemplateContext(w, r, models.TemplateData{}))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,9 +45,9 @@ func AddPrizesHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
func ModifyPrizesHandler(db *sql.DB) http.HandlerFunc {
|
func ModifyPrizesHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
tmpl := helpers.LoadTemplateFiles("modify_prizes.html", "templates/admin/draws/prizes/modify_prizes.html")
|
tmpl := templateHelpers.LoadTemplateFiles("modify_prizes.html", "templates/admin/draws/prizes/modify_prizes.html")
|
||||||
|
|
||||||
tmpl.ExecuteTemplate(w, "layout", helpers.TemplateContext(w, r, models.TemplateData{}))
|
tmpl.ExecuteTemplate(w, "layout", templateHelpers.TemplateContext(w, r, models.TemplateData{}))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"synlotto-website/helpers"
|
|
||||||
|
templateHandlers "synlotto-website/handlers/template"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Home(db *sql.DB) http.HandlerFunc {
|
func Home(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("index.html", "templates/index.html")
|
tmpl := templateHelpers.LoadTemplateFiles("index.html", "templates/index.html")
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
@@ -13,11 +15,11 @@ func NewDraw(db *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Println("➡️ New draw form opened")
|
log.Println("➡️ New draw form opened")
|
||||||
|
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
context["Page"] = "new_draw"
|
context["Page"] = "new_draw"
|
||||||
context["Data"] = nil
|
context["Data"] = nil
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("new_draw.html", "templates/new_draw.html") // ToDo: may need removing or moving add draw should be admin functionality and only when manually required. Potential live drawing of numbers in the future.
|
tmpl := templateHelpers.LoadTemplateFiles("new_draw.html", "templates/new_draw.html") // ToDo: may need removing or moving add draw should be admin functionality and only when manually required. Potential live drawing of numbers in the future.
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -5,6 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
templateHandlers "synlotto-website/handlers/template"
|
||||||
|
securityHelpers "synlotto-website/helpers/security"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
"synlotto-website/storage"
|
"synlotto-website/storage"
|
||||||
@@ -14,18 +19,18 @@ func CreateSyndicateHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
tmpl := helpers.LoadTemplateFiles("create-syndicate.html", "templates/syndicate/create.html")
|
tmpl := templateHelpers.LoadTemplateFiles("create-syndicate.html", "templates/syndicate/create.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
|
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
name := r.FormValue("name")
|
name := r.FormValue("name")
|
||||||
description := r.FormValue("description")
|
description := r.FormValue("description")
|
||||||
|
|
||||||
userId, ok := helpers.GetCurrentUserID(r)
|
userId, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok || name == "" {
|
if !ok || name == "" {
|
||||||
helpers.SetFlash(w, r, "Invalid data submitted")
|
templateHelpers.SetFlash(w, r, "Invalid data submitted")
|
||||||
http.Redirect(w, r, "/syndicate/create", http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate/create", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -33,23 +38,23 @@ func CreateSyndicateHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
_, err := storage.CreateSyndicate(db, userId, name, description)
|
_, err := storage.CreateSyndicate(db, userId, name, description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ CreateSyndicate failed: %v", err)
|
log.Printf("❌ CreateSyndicate failed: %v", err)
|
||||||
helpers.SetFlash(w, r, "Failed to create syndicate")
|
templateHelpers.SetFlash(w, r, "Failed to create syndicate")
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "Syndicate created successfully")
|
templateHelpers.SetFlash(w, r, "Syndicate created successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||||
default:
|
default:
|
||||||
helpers.RenderError(w, r, http.StatusMethodNotAllowed)
|
templateHelpers.RenderError(w, r, http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListSyndicatesHandler(db *sql.DB) http.HandlerFunc {
|
func ListSyndicatesHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403) // ToDo need to make this use the handler so i dont need to define errors.
|
templateHelpers.RenderError(w, r, 403) // ToDo need to make this use the handler so i dont need to define errors.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,28 +73,28 @@ func ListSyndicatesHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["ManagedSyndicates"] = managed
|
context["ManagedSyndicates"] = managed
|
||||||
context["JoinedSyndicates"] = filteredJoined
|
context["JoinedSyndicates"] = filteredJoined
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("syndicates.html", "templates/syndicate/index.html")
|
tmpl := templateHelpers.LoadTemplateFiles("syndicates.html", "templates/syndicate/index.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ViewSyndicateHandler(db *sql.DB) http.HandlerFunc {
|
func ViewSyndicateHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
syndicate, err := storage.GetSyndicateByID(db, syndicateID)
|
syndicate, err := storage.GetSyndicateByID(db, syndicateID)
|
||||||
if err != nil || syndicate == nil {
|
if err != nil || syndicate == nil {
|
||||||
helpers.RenderError(w, r, 404)
|
templateHelpers.RenderError(w, r, 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,45 +102,45 @@ func ViewSyndicateHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
isMember := storage.IsSyndicateMember(db, syndicateID, userID)
|
isMember := storage.IsSyndicateMember(db, syndicateID, userID)
|
||||||
|
|
||||||
if !isManager && !isMember {
|
if !isManager && !isMember {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
members := storage.GetSyndicateMembers(db, syndicateID)
|
members := storage.GetSyndicateMembers(db, syndicateID)
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Syndicate"] = syndicate
|
context["Syndicate"] = syndicate
|
||||||
context["Members"] = members
|
context["Members"] = members
|
||||||
context["IsManager"] = isManager
|
context["IsManager"] = isManager
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("syndicate-view.html", "templates/syndicate/view.html")
|
tmpl := templateHelpers.LoadTemplateFiles("syndicate-view.html", "templates/syndicate/view.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SyndicateLogTicketHandler(db *sql.DB) http.HandlerFunc {
|
func SyndicateLogTicketHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syndicateId := helpers.Atoi(r.URL.Query().Get("id"))
|
syndicateId := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
syndicate, err := storage.GetSyndicateByID(db, syndicateId)
|
syndicate, err := storage.GetSyndicateByID(db, syndicateId)
|
||||||
if err != nil || syndicate.OwnerID != userID {
|
if err != nil || syndicate.OwnerID != userID {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Syndicate"] = syndicate
|
context["Syndicate"] = syndicate
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("syndicate-log-ticket.html", "templates/syndicate/log_ticket.html")
|
tmpl := templateHelpers.LoadTemplateFiles("syndicate-log-ticket.html", "templates/syndicate/log_ticket.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
|
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
@@ -153,46 +158,46 @@ func SyndicateLogTicketHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to add ticket.")
|
templateHelpers.SetFlash(w, r, "Failed to add ticket.")
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "Ticket added for syndicate.")
|
templateHelpers.SetFlash(w, r, "Ticket added for syndicate.")
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, fmt.Sprintf("/syndicate/view?id=%d", syndicateId), http.StatusSeeOther)
|
http.Redirect(w, r, fmt.Sprintf("/syndicate/view?id=%d", syndicateId), http.StatusSeeOther)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
helpers.RenderError(w, r, 405)
|
templateHelpers.RenderError(w, r, 405)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SyndicateTicketsHandler(db *sql.DB) http.HandlerFunc {
|
func SyndicateTicketsHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
if syndicateID == 0 {
|
if syndicateID == 0 {
|
||||||
helpers.RenderError(w, r, 400)
|
templateHelpers.RenderError(w, r, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !storage.IsSyndicateMember(db, syndicateID, userID) {
|
if !storage.IsSyndicateMember(db, syndicateID, userID) {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tickets := storage.GetSyndicateTickets(db, syndicateID)
|
tickets := storage.GetSyndicateTickets(db, syndicateID)
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["SyndicateID"] = syndicateID
|
context["SyndicateID"] = syndicateID
|
||||||
context["Tickets"] = tickets
|
context["Tickets"] = tickets
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("syndicate-tickets.html", "templates/syndicate/tickets.html")
|
tmpl := templateHelpers.LoadTemplateFiles("syndicate-tickets.html", "templates/syndicate/tickets.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,29 +7,33 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
templateHandlers "synlotto-website/handlers/template"
|
||||||
|
securityHelpers "synlotto-website/helpers/security"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/storage"
|
"synlotto-website/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc {
|
func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, http.StatusForbidden)
|
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["SyndicateID"] = syndicateID
|
context["SyndicateID"] = syndicateID
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("invite-syndicate.html", "templates/syndicate/invite.html")
|
tmpl := templateHelpers.LoadTemplateFiles("invite-syndicate.html", "templates/syndicate/invite.html")
|
||||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.RenderError(w, r, 500)
|
templateHelpers.RenderError(w, r, 500)
|
||||||
}
|
}
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
syndicateID := helpers.Atoi(r.FormValue("syndicate_id"))
|
syndicateID := helpers.Atoi(r.FormValue("syndicate_id"))
|
||||||
@@ -37,32 +41,32 @@ func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
err := storage.InviteToSyndicate(db, userID, syndicateID, username)
|
err := storage.InviteToSyndicate(db, userID, syndicateID, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to send invite: "+err.Error())
|
templateHelpers.SetFlash(w, r, "Failed to send invite: "+err.Error())
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "Invite sent successfully.")
|
templateHelpers.SetFlash(w, r, "Invite sent successfully.")
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
helpers.RenderError(w, r, http.StatusMethodNotAllowed)
|
templateHelpers.RenderError(w, r, http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ViewInvitesHandler(db *sql.DB) http.HandlerFunc {
|
func ViewInvitesHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
invites := storage.GetPendingInvites(db, userID)
|
invites := storage.GetPendingInvites(db, userID)
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Invites"] = invites
|
context["Invites"] = invites
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("invites.html", "templates/syndicate/invites.html")
|
tmpl := templateHelpers.LoadTemplateFiles("invites.html", "templates/syndicate/invites.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,16 +74,16 @@ func ViewInvitesHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
func AcceptInviteHandler(db *sql.DB) http.HandlerFunc {
|
func AcceptInviteHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
inviteID := helpers.Atoi(r.URL.Query().Get("id"))
|
inviteID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := storage.AcceptInvite(db, inviteID, userID)
|
err := storage.AcceptInvite(db, inviteID, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to accept invite")
|
templateHelpers.SetFlash(w, r, "Failed to accept invite")
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "You have joined the syndicate")
|
templateHelpers.SetFlash(w, r, "You have joined the syndicate")
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
@@ -94,7 +98,7 @@ func DeclineInviteHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateInviteToken(db *sql.DB, syndicateID, invitedByID int, ttlHours int) (string, error) {
|
func CreateInviteToken(db *sql.DB, syndicateID, invitedByID int, ttlHours int) (string, error) {
|
||||||
token, err := helpers.GenerateSecureToken()
|
token, err := securityHelpers.GenerateSecureToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -142,16 +146,16 @@ func AcceptInviteToken(db *sql.DB, token string, userID int) error {
|
|||||||
|
|
||||||
func GenerateInviteLinkHandler(db *sql.DB) http.HandlerFunc {
|
func GenerateInviteLinkHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, http.StatusForbidden)
|
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
token, err := CreateInviteToken(db, syndicateID, userID, 48) // token valid for 48 hours
|
token, err := CreateInviteToken(db, syndicateID, userID, 48)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to generate invite link.")
|
templateHelpers.SetFlash(w, r, "Failed to generate invite link.")
|
||||||
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -164,31 +168,31 @@ func GenerateInviteLinkHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
inviteLink := fmt.Sprintf("%s/syndicate/join?token=%s", origin, token)
|
inviteLink := fmt.Sprintf("%s/syndicate/join?token=%s", origin, token)
|
||||||
|
|
||||||
helpers.SetFlash(w, r, "Invite link created: "+inviteLink)
|
templateHelpers.SetFlash(w, r, "Invite link created: "+inviteLink)
|
||||||
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func JoinSyndicateWithTokenHandler(db *sql.DB) http.HandlerFunc {
|
func JoinSyndicateWithTokenHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, http.StatusForbidden)
|
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token := r.URL.Query().Get("token")
|
token := r.URL.Query().Get("token")
|
||||||
if token == "" {
|
if token == "" {
|
||||||
helpers.SetFlash(w, r, "Invalid or missing invite token.")
|
templateHelpers.SetFlash(w, r, "Invalid or missing invite token.")
|
||||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := AcceptInviteToken(db, token, userID)
|
err := AcceptInviteToken(db, token, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to join syndicate: "+err.Error())
|
templateHelpers.SetFlash(w, r, "Failed to join syndicate: "+err.Error())
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "You have joined the syndicate!")
|
templateHelpers.SetFlash(w, r, "You have joined the syndicate!")
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
@@ -196,27 +200,27 @@ func JoinSyndicateWithTokenHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
func ManageInviteTokensHandler(db *sql.DB) http.HandlerFunc {
|
func ManageInviteTokensHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
|
|
||||||
if !storage.IsSyndicateManager(db, syndicateID, userID) {
|
if !storage.IsSyndicateManager(db, syndicateID, userID) {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens := storage.GetInviteTokensForSyndicate(db, syndicateID)
|
tokens := storage.GetInviteTokensForSyndicate(db, syndicateID)
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Tokens"] = tokens
|
context["Tokens"] = tokens
|
||||||
context["SyndicateID"] = syndicateID
|
context["SyndicateID"] = syndicateID
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("invite-links.html", "templates/syndicate/invite_links.html")
|
tmpl := templateHelpers.LoadTemplateFiles("invite-links.html", "templates/syndicate/invite_links.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
securityHelpers "synlotto-website/helpers/security"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
draws "synlotto-website/services/draws"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
draws "synlotto-website/services/draws"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
)
|
)
|
||||||
@@ -39,11 +43,11 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
context["csrfField"] = csrf.TemplateField(r)
|
context["csrfField"] = csrf.TemplateField(r)
|
||||||
context["DrawDates"] = drawDates
|
context["DrawDates"] = drawDates
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("add_ticket.html", "templates/account/tickets/add_ticket.html")
|
tmpl := templateHelpers.LoadTemplateFiles("add_ticket.html", "templates/account/tickets/add_ticket.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -60,7 +64,7 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
@@ -183,7 +187,7 @@ func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
@@ -265,7 +269,7 @@ func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
||||||
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
return helpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
@@ -355,10 +359,10 @@ func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
|||||||
tickets = append(tickets, t)
|
tickets = append(tickets, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
context := helpers.TemplateContext(w, r, models.TemplateData{})
|
context := templateHelpers.TemplateContext(w, r, models.TemplateData{})
|
||||||
context["Tickets"] = tickets
|
context["Tickets"] = tickets
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("my_tickets.html", "templates/account/tickets/my_tickets.html")
|
tmpl := templateHelpers.LoadTemplateFiles("my_tickets.html", "templates/account/tickets/my_tickets.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -4,20 +4,26 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
templateHandlers "synlotto-website/handlers/template"
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
|
httpHelpers "synlotto-website/helpers/http"
|
||||||
|
securityHelpers "synlotto-website/helpers/security"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/storage"
|
"synlotto-website/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MessagesInboxHandler(db *sql.DB) http.HandlerFunc {
|
func MessagesInboxHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
page := helpers.Atoi(r.URL.Query().Get("page"))
|
page := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
@@ -31,18 +37,18 @@ func MessagesInboxHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
messages := storage.GetInboxMessages(db, userID, page, perPage)
|
messages := storage.GetInboxMessages(db, userID, page, perPage)
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
|
|
||||||
context["Messages"] = messages
|
context["Messages"] = messages
|
||||||
context["CurrentPage"] = page
|
context["CurrentPage"] = page
|
||||||
context["TotalPages"] = totalPages
|
context["TotalPages"] = totalPages
|
||||||
context["PageRange"] = helpers.PageRange(page, totalPages)
|
context["PageRange"] = templateHelpers.PageRange(page, totalPages)
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("messages.html", "templates/account/messages/index.html")
|
tmpl := templateHelpers.LoadTemplateFiles("messages.html", "templates/account/messages/index.html")
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil {
|
if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil {
|
||||||
helpers.RenderError(w, r, 500)
|
templateHelpers.RenderError(w, r, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,10 +58,10 @@ func ReadMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
idStr := r.URL.Query().Get("id")
|
idStr := r.URL.Query().Get("id")
|
||||||
messageID := helpers.Atoi(idStr)
|
messageID := helpers.Atoi(idStr)
|
||||||
|
|
||||||
session, _ := helpers.GetSession(w, r)
|
session, _ := httpHelpers.GetSession(w, r)
|
||||||
userID, ok := session.Values["user_id"].(int)
|
userID, ok := session.Values["user_id"].(int)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,11 +73,11 @@ func ReadMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
_ = storage.MarkMessageAsRead(db, messageID, userID)
|
_ = storage.MarkMessageAsRead(db, messageID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Message"] = message
|
context["Message"] = message
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("read-message.html", "templates/account/messages/read.html")
|
tmpl := templateHelpers.LoadTemplateFiles("read-message.html", "templates/account/messages/read.html")
|
||||||
|
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
@@ -80,17 +86,17 @@ func ReadMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
func ArchiveMessageHandler(db *sql.DB) http.HandlerFunc {
|
func ArchiveMessageHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := helpers.Atoi(r.URL.Query().Get("id"))
|
id := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := storage.ArchiveMessage(db, userID, id)
|
err := storage.ArchiveMessage(db, userID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to archive message.")
|
templateHelpers.SetFlash(w, r, "Failed to archive message.")
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "Message archived.")
|
templateHelpers.SetFlash(w, r, "Message archived.")
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/account/messages", http.StatusSeeOther)
|
http.Redirect(w, r, "/account/messages", http.StatusSeeOther)
|
||||||
@@ -99,9 +105,9 @@ func ArchiveMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
func ArchivedMessagesHandler(db *sql.DB) http.HandlerFunc {
|
func ArchivedMessagesHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,13 +120,13 @@ func ArchivedMessagesHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
messages := storage.GetArchivedMessages(db, userID, page, perPage)
|
messages := storage.GetArchivedMessages(db, userID, page, perPage)
|
||||||
hasMore := len(messages) == perPage
|
hasMore := len(messages) == perPage
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Messages"] = messages
|
context["Messages"] = messages
|
||||||
context["Page"] = page
|
context["Page"] = page
|
||||||
context["HasMore"] = hasMore
|
context["HasMore"] = hasMore
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("archived.html", "templates/account/messages/archived.html")
|
tmpl := templateHelpers.LoadTemplateFiles("archived.html", "templates/account/messages/archived.html")
|
||||||
tmpl.ExecuteTemplate(w, "layout", context)
|
tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,19 +135,17 @@ func SendMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
// Display the form
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
data := BuildTemplateData(db, w, r)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
tmpl := templateHelpers.LoadTemplateFiles("send-message.html", "templates/account/messages/send.html")
|
||||||
tmpl := helpers.LoadTemplateFiles("send-message.html", "templates/account/messages/send.html")
|
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil {
|
if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil {
|
||||||
helpers.RenderError(w, r, 500)
|
templateHelpers.RenderError(w, r, 500)
|
||||||
}
|
}
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
// Handle form submission
|
senderID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
senderID, ok := helpers.GetCurrentUserID(r)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,13 +154,13 @@ func SendMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
body := r.FormValue("message")
|
body := r.FormValue("message")
|
||||||
|
|
||||||
if err := storage.SendMessage(db, senderID, recipientID, subject, body); err != nil {
|
if err := storage.SendMessage(db, senderID, recipientID, subject, body); err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to send message.")
|
templateHelpers.SetFlash(w, r, "Failed to send message.")
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "Message sent.")
|
templateHelpers.SetFlash(w, r, "Message sent.")
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/account/messages", http.StatusSeeOther)
|
http.Redirect(w, r, "/account/messages", http.StatusSeeOther)
|
||||||
default:
|
default:
|
||||||
helpers.RenderError(w, r, 405)
|
templateHelpers.RenderError(w, r, 405)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,17 +168,17 @@ func SendMessageHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
func RestoreMessageHandler(db *sql.DB) http.HandlerFunc {
|
func RestoreMessageHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := helpers.Atoi(r.URL.Query().Get("id"))
|
id := helpers.Atoi(r.URL.Query().Get("id"))
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
helpers.RenderError(w, r, 403)
|
templateHelpers.RenderError(w, r, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := storage.RestoreMessage(db, userID, id)
|
err := storage.RestoreMessage(db, userID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SetFlash(w, r, "Failed to restore message.")
|
templateHelpers.SetFlash(w, r, "Failed to restore message.")
|
||||||
} else {
|
} else {
|
||||||
helpers.SetFlash(w, r, "Message restored.")
|
templateHelpers.SetFlash(w, r, "Message restored.")
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/account/messages/archived", http.StatusSeeOther)
|
http.Redirect(w, r, "/account/messages/archived", http.StatusSeeOther)
|
||||||
|
|||||||
@@ -6,16 +6,19 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
templateHandlers "synlotto-website/handlers/template"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/storage"
|
"synlotto-website/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NotificationsHandler(db *sql.DB) http.HandlerFunc {
|
func NotificationsHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("index.html", "templates/account/notifications/index.html")
|
tmpl := templateHelpers.LoadTemplateFiles("index.html", "templates/account/notifications/index.html")
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -52,11 +55,11 @@ func MarkNotificationReadHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := BuildTemplateData(db, w, r)
|
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||||
context := helpers.TemplateContext(w, r, data)
|
context := templateHelpers.TemplateContext(w, r, data)
|
||||||
context["Notification"] = notification
|
context["Notification"] = notification
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("read.html", "templates/account/notifications/read.html")
|
tmpl := templateHelpers.LoadTemplateFiles("read.html", "templates/account/notifications/read.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/middleware"
|
"synlotto-website/middleware"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
@@ -111,7 +113,7 @@ func ResultsThunderball(db *sql.DB) http.HandlerFunc {
|
|||||||
noResultsMsg = "No results found for \"" + query + "\""
|
noResultsMsg = "No results found for \"" + query + "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := helpers.LoadTemplateFiles("thunderball.html", "templates/results/thunderball.html")
|
tmpl := templateHelpers.LoadTemplateFiles("thunderball.html", "templates/results/thunderball.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
err = tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
||||||
"Results": results,
|
"Results": results,
|
||||||
|
|||||||
@@ -2,14 +2,20 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"synlotto-website/helpers"
|
|
||||||
|
httpHelper "synlotto-website/helpers/http"
|
||||||
|
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
"synlotto-website/storage"
|
"synlotto-website/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BuildTemplateData(db *sql.DB, w http.ResponseWriter, r *http.Request) models.TemplateData {
|
func BuildTemplateData(db *sql.DB, w http.ResponseWriter, r *http.Request) models.TemplateData {
|
||||||
session, _ := helpers.GetSession(w, r)
|
session, err := httpHelper.GetSession(w, r)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Session error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
var user *models.User
|
var user *models.User
|
||||||
var isAdmin bool
|
var isAdmin bool
|
||||||
@@ -18,13 +24,8 @@ func BuildTemplateData(db *sql.DB, w http.ResponseWriter, r *http.Request) model
|
|||||||
var messageCount int
|
var messageCount int
|
||||||
var messages []models.Message
|
var messages []models.Message
|
||||||
|
|
||||||
switch v := session.Values["user_id"].(type) {
|
if userId, ok := session.Values["user_id"].(int); ok {
|
||||||
case int:
|
user = storage.GetUserByID(db, userId)
|
||||||
user = models.GetUserByID(v) // ToDo should be storage not models
|
|
||||||
case int64:
|
|
||||||
user = models.GetUserByID(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if user != nil {
|
if user != nil {
|
||||||
isAdmin = user.IsAdmin
|
isAdmin = user.IsAdmin
|
||||||
notificationCount = storage.GetNotificationCount(db, user.Id)
|
notificationCount = storage.GetNotificationCount(db, user.Id)
|
||||||
@@ -32,6 +33,7 @@ func BuildTemplateData(db *sql.DB, w http.ResponseWriter, r *http.Request) model
|
|||||||
messageCount, _ = storage.GetMessageCount(db, user.Id)
|
messageCount, _ = storage.GetMessageCount(db, user.Id)
|
||||||
messages = storage.GetRecentMessages(db, user.Id, 15)
|
messages = storage.GetRecentMessages(db, user.Id, 15)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return models.TemplateData{
|
return models.TemplateData{
|
||||||
User: user,
|
User: user,
|
||||||
51
helpers/http/session.go
Normal file
51
helpers/http/session.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
session "synlotto-website/handlers/session"
|
||||||
|
|
||||||
|
"synlotto-website/constants"
|
||||||
|
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSession(w http.ResponseWriter, r *http.Request) (*sessions.Session, error) {
|
||||||
|
return session.GetSession(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSessionExpired(session *sessions.Session) bool {
|
||||||
|
last, ok := session.Values["last_activity"].(time.Time)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return time.Since(last) > constants.SessionDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSessionActivity(session *sessions.Session, r *http.Request, w http.ResponseWriter) {
|
||||||
|
session.Values["last_activity"] = time.Now().UTC()
|
||||||
|
session.Save(r, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
session, _ := GetSession(w, r)
|
||||||
|
|
||||||
|
if IsSessionExpired(session) {
|
||||||
|
session.Options.MaxAge = -1
|
||||||
|
session.Save(r, w)
|
||||||
|
|
||||||
|
newSession, _ := GetSession(w, r)
|
||||||
|
newSession.Values["flash"] = "Your session has timed out."
|
||||||
|
newSession.Save(r, w)
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/account/login", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSessionActivity(session, r, w)
|
||||||
|
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package helpers
|
package security
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package helpers
|
package security
|
||||||
|
|
||||||
import "golang.org/x/crypto/bcrypt"
|
import "golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package helpers
|
package security
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
17
helpers/security/users.go
Normal file
17
helpers/security/users.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
httpHelpers "synlotto-website/helpers/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetCurrentUserID(r *http.Request) (int, bool) {
|
||||||
|
session, err := httpHelpers.GetSession(nil, r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
id, ok := session.Values["user_id"].(int)
|
||||||
|
return id, ok
|
||||||
|
}
|
||||||
@@ -65,13 +65,3 @@ func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
next(w, r)
|
next(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCurrentUserID(r *http.Request) (int, bool) {
|
|
||||||
session, err := GetSession(nil, r)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
id, ok := session.Values["user_id"].(int)
|
|
||||||
return id, ok
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,14 +5,21 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"synlotto-website/config"
|
||||||
|
helpers "synlotto-website/helpers/http"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
|
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TemplateContext(w http.ResponseWriter, r *http.Request, data models.TemplateData) map[string]interface{} {
|
func TemplateContext(w http.ResponseWriter, r *http.Request, data models.TemplateData) map[string]interface{} {
|
||||||
session, _ := GetSession(w, r)
|
cfg := config.Get()
|
||||||
|
if cfg == nil {
|
||||||
|
log.Println("⚠️ Config not initialized!")
|
||||||
|
}
|
||||||
|
session, _ := helpers.GetSession(w, r)
|
||||||
|
|
||||||
var flash string
|
var flash string
|
||||||
if f, ok := session.Values["flash"].(string); ok {
|
if f, ok := session.Values["flash"].(string); ok {
|
||||||
@@ -30,6 +37,8 @@ func TemplateContext(w http.ResponseWriter, r *http.Request, data models.Templat
|
|||||||
"Notifications": data.Notifications,
|
"Notifications": data.Notifications,
|
||||||
"MessageCount": data.MessageCount,
|
"MessageCount": data.MessageCount,
|
||||||
"Messages": data.Messages,
|
"Messages": data.Messages,
|
||||||
|
"SiteName": cfg.Site.SiteName,
|
||||||
|
"YearStart": cfg.Site.CopyrightStart,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +69,6 @@ func TemplateFuncs() template.FuncMap {
|
|||||||
},
|
},
|
||||||
"inSlice": InSlice,
|
"inSlice": InSlice,
|
||||||
"lower": lower,
|
"lower": lower,
|
||||||
"rangeClass": rangeClass,
|
|
||||||
"truncate": func(s string, max int) string {
|
"truncate": func(s string, max int) string {
|
||||||
if len(s) <= max {
|
if len(s) <= max {
|
||||||
return s
|
return s
|
||||||
@@ -68,22 +76,37 @@ func TemplateFuncs() template.FuncMap {
|
|||||||
return s[:max] + "..."
|
return s[:max] + "..."
|
||||||
},
|
},
|
||||||
"PageRange": PageRange,
|
"PageRange": PageRange,
|
||||||
|
"now": time.Now,
|
||||||
|
"humanTime": func(v interface{}) string {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case time.Time:
|
||||||
|
return t.Local().Format("02 Jan 2006 15:04")
|
||||||
|
case string:
|
||||||
|
parsed, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err == nil {
|
||||||
|
return parsed.Local().Format("02 Jan 2006 15:04")
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadTemplateFiles(name string, files ...string) *template.Template {
|
func LoadTemplateFiles(name string, files ...string) *template.Template {
|
||||||
shared := []string{
|
shared := []string{
|
||||||
"templates/layout.html",
|
"templates/main/layout.html",
|
||||||
"templates/topbar.html",
|
"templates/main/topbar.html",
|
||||||
|
"templates/main/footer.html",
|
||||||
}
|
}
|
||||||
all := append(shared, files...)
|
all := append(shared, files...)
|
||||||
|
|
||||||
log.Printf("📄 Loading templates: %v", all)
|
|
||||||
return template.Must(template.New(name).Funcs(TemplateFuncs()).ParseFiles(all...))
|
return template.Must(template.New(name).Funcs(TemplateFuncs()).ParseFiles(all...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetFlash(w http.ResponseWriter, r *http.Request, message string) {
|
func SetFlash(w http.ResponseWriter, r *http.Request, message string) {
|
||||||
session, _ := GetSession(w, r)
|
session, _ := helpers.GetSession(w, r)
|
||||||
session.Values["flash"] = message
|
session.Values["flash"] = message
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
}
|
}
|
||||||
@@ -101,6 +124,15 @@ func lower(input string) string {
|
|||||||
return strings.ToLower(input)
|
return strings.ToLower(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PageRange(current, total int) []int {
|
||||||
|
var pages []int
|
||||||
|
for i := 1; i <= total; i++ {
|
||||||
|
pages = append(pages, i)
|
||||||
|
}
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: Should be ball range class, and should it even be here?
|
||||||
func rangeClass(n int) string {
|
func rangeClass(n int) string {
|
||||||
switch {
|
switch {
|
||||||
case n >= 1 && n <= 9:
|
case n >= 1 && n <= 9:
|
||||||
@@ -117,11 +149,3 @@ func rangeClass(n int) string {
|
|||||||
return "50-plus"
|
return "50-plus"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PageRange(current, total int) []int {
|
|
||||||
var pages []int
|
|
||||||
for i := 1; i <= total; i++ {
|
|
||||||
pages = append(pages, i)
|
|
||||||
}
|
|
||||||
return pages
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,5 +37,3 @@ func RenderError(w http.ResponseWriter, r *http.Request, statusCode int) {
|
|||||||
|
|
||||||
log.Println("✅ Successfully rendered error page") // ToDo: log these to database
|
log.Println("✅ Successfully rendered error page") // ToDo: log these to database
|
||||||
}
|
}
|
||||||
|
|
||||||
//ToDo Pages.go /template.go to be merged?
|
|
||||||
25
internal/licensecheck/checker.go
Normal file
25
internal/licensecheck/checker.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LicenseChecker struct {
|
||||||
|
LicenseAPIURL string
|
||||||
|
APIKey string
|
||||||
|
PollInterval time.Duration
|
||||||
|
|
||||||
|
mu sync.RWMutex
|
||||||
|
lastGood time.Time
|
||||||
|
valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *LicenseChecker) setValid(ok bool) {
|
||||||
|
lc.mu.Lock()
|
||||||
|
defer lc.mu.Unlock()
|
||||||
|
lc.valid = ok
|
||||||
|
if ok {
|
||||||
|
lc.lastGood = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
76
internal/licensecheck/validate.go
Normal file
76
internal/licensecheck/validate.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (lc *LicenseChecker) Validate() error {
|
||||||
|
url := fmt.Sprintf("%s/license/lookup?key=%s&format=json", lc.LicenseAPIURL, lc.APIKey)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
lc.setValid(false)
|
||||||
|
|
||||||
|
return fmt.Errorf("license lookup failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
lc.setValid(false)
|
||||||
|
|
||||||
|
return fmt.Errorf("license lookup error: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
lc.setValid(false)
|
||||||
|
|
||||||
|
return fmt.Errorf("reading response failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Revoked bool `json:"revoked"`
|
||||||
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &data); err != nil {
|
||||||
|
lc.setValid(false)
|
||||||
|
|
||||||
|
return fmt.Errorf("unmarshal error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Revoked || time.Now().After(data.ExpiresAt) {
|
||||||
|
lc.setValid(false)
|
||||||
|
|
||||||
|
return fmt.Errorf("license expired or revoked")
|
||||||
|
}
|
||||||
|
|
||||||
|
lc.mu.Lock()
|
||||||
|
lc.valid = true
|
||||||
|
lc.lastGood = time.Now()
|
||||||
|
lc.mu.Unlock()
|
||||||
|
|
||||||
|
log.Printf("✅ License validated. Expires: %s", data.ExpiresAt)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *LicenseChecker) StartBackgroundCheck() {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(lc.PollInterval)
|
||||||
|
err := lc.Validate()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("⚠️ License check failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lc *LicenseChecker) IsValid() bool {
|
||||||
|
lc.mu.RLock()
|
||||||
|
defer lc.mu.RUnlock()
|
||||||
|
return lc.valid
|
||||||
|
}
|
||||||
4
main.go
4
main.go
@@ -34,6 +34,10 @@ func main() {
|
|||||||
logging.Error("❌ Failed to init session: %v", err)
|
logging.Error("❌ Failed to init session: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if err := bootstrap.InitLicenseChecker(appState.Config); err != nil {
|
||||||
|
// logging.Error("❌ Invalid license: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
err = bootstrap.InitCSRFProtection([]byte(appState.Config.CSRF.CSRFKey), appState.Config.HttpServer.ProductionMode)
|
err = bootstrap.InitCSRFProtection([]byte(appState.Config.CSRF.CSRFKey), appState.Config.HttpServer.ProductionMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Error("Failed to init CSRF: %v", err)
|
logging.Error("Failed to init CSRF: %v", err)
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"synlotto-website/helpers"
|
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Recover(next http.Handler) http.Handler {
|
func Recover(next http.Handler) http.Handler {
|
||||||
@@ -13,7 +14,7 @@ func Recover(next http.Handler) http.Handler {
|
|||||||
if rec := recover(); rec != nil {
|
if rec := recover(); rec != nil {
|
||||||
log.Printf("🔥 Recovered from panic: %v\n%s", rec, debug.Stack())
|
log.Printf("🔥 Recovered from panic: %v\n%s", rec, debug.Stack())
|
||||||
|
|
||||||
helpers.RenderError(w, r, http.StatusInternalServerError)
|
templateHelpers.RenderError(w, r, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ type Config struct {
|
|||||||
CSRFKey string `json:"csrfKey"`
|
CSRFKey string `json:"csrfKey"`
|
||||||
} `json:"csrf"`
|
} `json:"csrf"`
|
||||||
|
|
||||||
|
License struct {
|
||||||
|
APIURL string `json:"apiUrl"`
|
||||||
|
APIKey string `json:"apiKey"`
|
||||||
|
} `json:"license"`
|
||||||
|
|
||||||
Session struct {
|
Session struct {
|
||||||
AuthKeyPath string `json:"authKeyPath"`
|
AuthKeyPath string `json:"authKeyPath"`
|
||||||
EncryptionKeyPath string `json:"encryptionKeyPath"`
|
EncryptionKeyPath string `json:"encryptionKeyPath"`
|
||||||
|
|||||||
@@ -59,35 +59,3 @@ func GetUserByUsername(username string) *User {
|
|||||||
|
|
||||||
return &user
|
return &user
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByID(id int) *User {
|
|
||||||
row := db.QueryRow("SELECT id, username, password_hash, is_admin FROM users WHERE id = ?", id)
|
|
||||||
|
|
||||||
var user User
|
|
||||||
err := row.Scan(&user.Id, &user.Username, &user.PasswordHash, &user.IsAdmin)
|
|
||||||
if err != nil {
|
|
||||||
if err != sql.ErrNoRows {
|
|
||||||
log.Println("DB error:", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &user
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogLoginAttempt(username string, success bool) {
|
|
||||||
_, err := db.Exec("INSERT INTO auditlog (username, success, timestamp) VALUES (?, ?, ?)",
|
|
||||||
username, boolToInt(success), time.Now().Format(time.RFC3339)) // tOdO: SHOULD BE USING UTC
|
|
||||||
if err != nil {
|
|
||||||
log.Println("❌ Failed to log login:", err)
|
|
||||||
}
|
|
||||||
} // ToDo this shouldn't be in models. Also why did i build a bool to int? just use the bool
|
|
||||||
|
|
||||||
func boolToInt(b bool) int {
|
|
||||||
if b {
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
account "synlotto-website/handlers/account"
|
||||||
|
|
||||||
"synlotto-website/handlers"
|
"synlotto-website/handlers"
|
||||||
"synlotto-website/middleware"
|
"synlotto-website/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetupAccountRoutes(mux *http.ServeMux, db *sql.DB) {
|
func SetupAccountRoutes(mux *http.ServeMux, db *sql.DB) {
|
||||||
mux.HandleFunc("/login", middleware.Protected(handlers.Login))
|
mux.HandleFunc("/login", middleware.Protected(account.Login))
|
||||||
mux.HandleFunc("/logout", handlers.Logout)
|
mux.HandleFunc("/logout", account.Logout)
|
||||||
mux.HandleFunc("/signup", middleware.Protected(handlers.Signup))
|
mux.HandleFunc("/signup", middleware.Protected(account.Signup))
|
||||||
mux.HandleFunc("/account/tickets/add_ticket", handlers.AddTicket(db))
|
mux.HandleFunc("/account/tickets/add_ticket", handlers.AddTicket(db))
|
||||||
mux.HandleFunc("/account/tickets/my_tickets", handlers.GetMyTickets(db))
|
mux.HandleFunc("/account/tickets/my_tickets", handlers.GetMyTickets(db))
|
||||||
mux.HandleFunc("/account/messages", middleware.Protected(handlers.MessagesInboxHandler(db)))
|
mux.HandleFunc("/account/messages", middleware.Protected(handlers.MessagesInboxHandler(db)))
|
||||||
|
|||||||
@@ -4,21 +4,22 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"synlotto-website/handlers"
|
lotterySyndicateHandlers "synlotto-website/handlers/lottery/syndicate"
|
||||||
|
|
||||||
"synlotto-website/middleware"
|
"synlotto-website/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetupSyndicateRoutes(mux *http.ServeMux, db *sql.DB) {
|
func SetupSyndicateRoutes(mux *http.ServeMux, db *sql.DB) {
|
||||||
mux.HandleFunc("/syndicate", middleware.Auth(true)(handlers.ListSyndicatesHandler(db)))
|
mux.HandleFunc("/syndicate", middleware.Auth(true)(lotterySyndicateHandlers.ListSyndicatesHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/create", middleware.Auth(true)(handlers.CreateSyndicateHandler(db)))
|
mux.HandleFunc("/syndicate/create", middleware.Auth(true)(lotterySyndicateHandlers.CreateSyndicateHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/view", middleware.Auth(true)(handlers.ViewSyndicateHandler(db)))
|
mux.HandleFunc("/syndicate/view", middleware.Auth(true)(lotterySyndicateHandlers.ViewSyndicateHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/tickets", middleware.Auth(true)(handlers.SyndicateTicketsHandler(db)))
|
mux.HandleFunc("/syndicate/tickets", middleware.Auth(true)(lotterySyndicateHandlers.SyndicateTicketsHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/tickets/new", middleware.Auth(true)(handlers.SyndicateLogTicketHandler(db)))
|
mux.HandleFunc("/syndicate/tickets/new", middleware.Auth(true)(lotterySyndicateHandlers.SyndicateLogTicketHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/invites", middleware.Auth(true)(handlers.ViewInvitesHandler(db)))
|
mux.HandleFunc("/syndicate/invites", middleware.Auth(true)(lotterySyndicateHandlers.ViewInvitesHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/invites/accept", middleware.Auth(true)(handlers.AcceptInviteHandler(db)))
|
mux.HandleFunc("/syndicate/invites/accept", middleware.Auth(true)(lotterySyndicateHandlers.AcceptInviteHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/invites/decline", middleware.Auth(true)(handlers.DeclineInviteHandler(db)))
|
mux.HandleFunc("/syndicate/invites/decline", middleware.Auth(true)(lotterySyndicateHandlers.DeclineInviteHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/invite/token", middleware.Auth(true)(handlers.GenerateInviteLinkHandler(db)))
|
mux.HandleFunc("/syndicate/invite/token", middleware.Auth(true)(lotterySyndicateHandlers.GenerateInviteLinkHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/invite/tokens", middleware.Auth(true)(handlers.ManageInviteTokensHandler(db)))
|
mux.HandleFunc("/syndicate/invite/tokens", middleware.Auth(true)(lotterySyndicateHandlers.ManageInviteTokensHandler(db)))
|
||||||
mux.HandleFunc("/syndicate/join", middleware.Auth(true)(handlers.JoinSyndicateWithTokenHandler(db)))
|
mux.HandleFunc("/syndicate/join", middleware.Auth(true)(lotterySyndicateHandlers.JoinSyndicateWithTokenHandler(db)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"synlotto-website/handlers"
|
|
||||||
|
lotteryTicketHandlers "synlotto-website/handlers/lottery/tickets"
|
||||||
|
thunderballrules "synlotto-website/rules"
|
||||||
|
services "synlotto-website/services/draws"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
"synlotto-website/helpers"
|
||||||
"synlotto-website/matcher"
|
"synlotto-website/matcher"
|
||||||
"synlotto-website/models"
|
"synlotto-website/models"
|
||||||
thunderballrules "synlotto-website/rules"
|
|
||||||
services "synlotto-website/services/draws"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, error) {
|
func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, error) {
|
||||||
@@ -27,7 +29,6 @@ func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, er
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
// Buffer results to avoid writing while iterating
|
|
||||||
var pending []models.Ticket
|
var pending []models.Ticket
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
@@ -64,7 +65,7 @@ func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
draw := services.GetDrawResultForTicket(db, t.GameType, t.DrawDate)
|
draw := services.GetDrawResultForTicket(db, t.GameType, t.DrawDate)
|
||||||
result := handlers.MatchTicketToDraw(matchTicket, draw, thunderballrules.ThunderballPrizeRules)
|
result := lotteryTicketHandlers.MatchTicketToDraw(matchTicket, draw, thunderballrules.ThunderballPrizeRules)
|
||||||
|
|
||||||
if result.MatchedDrawID == 0 {
|
if result.MatchedDrawID == 0 {
|
||||||
continue
|
continue
|
||||||
@@ -105,7 +106,6 @@ func UpdateMissingPrizes(db *sql.DB) error {
|
|||||||
|
|
||||||
var tickets []TicketInfo
|
var tickets []TicketInfo
|
||||||
|
|
||||||
// Step 1: Load all relevant tickets
|
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
SELECT id, game_type, draw_date, matched_main, matched_bonus
|
SELECT id, game_type, draw_date, matched_main, matched_bonus
|
||||||
FROM my_tickets
|
FROM my_tickets
|
||||||
@@ -125,7 +125,6 @@ func UpdateMissingPrizes(db *sql.DB) error {
|
|||||||
tickets = append(tickets, t)
|
tickets = append(tickets, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Now that the reader is closed, perform updates
|
|
||||||
for _, t := range tickets {
|
for _, t := range tickets {
|
||||||
if t.GameType != "Thunderball" {
|
if t.GameType != "Thunderball" {
|
||||||
continue
|
continue
|
||||||
@@ -196,7 +195,7 @@ func RefreshTicketPrizes(db *sql.DB) error {
|
|||||||
}
|
}
|
||||||
tickets = append(tickets, t)
|
tickets = append(tickets, t)
|
||||||
}
|
}
|
||||||
rows.Close() // ✅ Release read lock before updating
|
rows.Close()
|
||||||
|
|
||||||
for _, row := range tickets {
|
for _, row := range tickets {
|
||||||
matchTicket := models.MatchTicket{
|
matchTicket := models.MatchTicket{
|
||||||
|
|||||||
@@ -5,16 +5,18 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"synlotto-website/helpers"
|
securityHelpers "synlotto-website/helpers/security"
|
||||||
|
templateHelpers "synlotto-website/helpers/template"
|
||||||
|
|
||||||
"synlotto-website/middleware"
|
"synlotto-website/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AdminOnly(db *sql.DB, next http.HandlerFunc) http.HandlerFunc {
|
func AdminOnly(db *sql.DB, next http.HandlerFunc) http.HandlerFunc {
|
||||||
return middleware.Auth(true)(func(w http.ResponseWriter, r *http.Request) {
|
return middleware.Auth(true)(func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, ok := helpers.GetCurrentUserID(r)
|
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||||
if !ok || !helpers.IsAdmin(db, userID) {
|
if !ok || !securityHelpers.IsAdmin(db, userID) {
|
||||||
log.Printf("⛔️ Unauthorized admin attempt: user_id=%v, IP=%s, Path=%s", userID, r.RemoteAddr, r.URL.Path)
|
log.Printf("⛔️ Unauthorized admin attempt: user_id=%v, IP=%s, Path=%s", userID, r.RemoteAddr, r.URL.Path)
|
||||||
helpers.RenderError(w, r, http.StatusForbidden)
|
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,5 +38,3 @@ func AdminOnly(db *sql.DB, next http.HandlerFunc) http.HandlerFunc {
|
|||||||
next(w, r)
|
next(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo need to look into audit/access log tables and consolidate
|
|
||||||
|
|||||||
22
storage/audit.go
Normal file
22
storage/audit.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"synlotto-website/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LogLoginAttempt(r *http.Request, username string, success bool) {
|
||||||
|
ip := r.RemoteAddr
|
||||||
|
userAgent := r.UserAgent()
|
||||||
|
|
||||||
|
_, err := db.Exec(
|
||||||
|
`INSERT INTO audit_login (username, success, ip, user_agent, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?)`,
|
||||||
|
username, success, ip, userAgent, time.Now().UTC(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logging.Info("❌ Failed to log login:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,16 +98,6 @@ func IsSyndicateMember(db *sql.DB, syndicateID, userID int) bool {
|
|||||||
return err == nil && count > 0
|
return err == nil && count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByUsername(db *sql.DB, username string) *models.User {
|
|
||||||
row := db.QueryRow(`SELECT id, username, is_admin FROM users WHERE username = ?`, username) // ToDo: needs hash
|
|
||||||
var u models.User
|
|
||||||
err := row.Scan(&u.Id, &u.Username, &u.IsAdmin)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &u
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddMemberToSyndicate(db *sql.DB, syndicateID, userID int) error {
|
func AddMemberToSyndicate(db *sql.DB, syndicateID, userID int) error {
|
||||||
_, err := db.Exec(`
|
_, err := db.Exec(`
|
||||||
INSERT INTO syndicate_members (syndicate_id, user_id, joined_at)
|
INSERT INTO syndicate_members (syndicate_id, user_id, joined_at)
|
||||||
|
|||||||
34
storage/users.go
Normal file
34
storage/users.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"synlotto-website/logging"
|
||||||
|
"synlotto-website/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetUserByID(db *sql.DB, id int) *models.User {
|
||||||
|
row := db.QueryRow("SELECT id, username, password_hash, is_admin FROM users WHERE id = ?", id)
|
||||||
|
|
||||||
|
var user models.User
|
||||||
|
err := row.Scan(&user.Id, &user.Username, &user.PasswordHash, &user.IsAdmin)
|
||||||
|
if err != nil {
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
logging.Error("DB error:", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserByUsername(db *sql.DB, username string) *models.User {
|
||||||
|
row := db.QueryRow(`SELECT id, username, password_hash, is_admin FROM users WHERE username = ?`, username)
|
||||||
|
|
||||||
|
var u models.User
|
||||||
|
err := row.Scan(&u.Id, &u.Username, &u.PasswordHash, &u.IsAdmin)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &u
|
||||||
|
}
|
||||||
17
templates/main/footer.html
Normal file
17
templates/main/footer.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{{ define "footer" }}
|
||||||
|
<footer class="bg-light text-center text-muted py-3 mt-auto border-top">
|
||||||
|
<small>
|
||||||
|
© Copyright {{ .SiteName }}
|
||||||
|
{{ $currentYear := now.Year }}
|
||||||
|
{{ if eq .YearStart $currentYear }}
|
||||||
|
{{ $currentYear }}
|
||||||
|
{{ else }}
|
||||||
|
{{ .YearStart }} - {{ $currentYear }}
|
||||||
|
{{ end }}
|
||||||
|
All rights reserved.
|
||||||
|
| <a href="/legal/privacy">Privacy Policy</a> |
|
||||||
|
<a href="/legal/terms">Terms & Conditions</a> |
|
||||||
|
<a href="/contact">Contact Us</a>
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
|
{{ end }}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
{{ define "layout" }}
|
{{ define "layout" }}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>SynLotto</title>
|
<title>{{ .SiteName }}</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="/static/css/site.css">
|
<link rel="stylesheet" href="/static/css/site.css">
|
||||||
@@ -12,9 +13,9 @@
|
|||||||
<body class="d-flex flex-column min-vh-100">
|
<body class="d-flex flex-column min-vh-100">
|
||||||
<!-- Topbar -->
|
<!-- Topbar -->
|
||||||
{{ template "topbar" . }}
|
{{ template "topbar" . }}
|
||||||
<!-- Main Layout -->
|
|
||||||
<div class="container-fluid flex-grow-1">
|
<!-- Main layout using Flexbox -->
|
||||||
<div class="row">
|
<div class="d-flex flex-grow-1">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<nav class="col-md-2 d-none d-md-block bg-light sidebar pt-3">
|
<nav class="col-md-2 d-none d-md-block bg-light sidebar pt-3">
|
||||||
<div class="position-sticky">
|
<div class="position-sticky">
|
||||||
@@ -62,8 +63,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 pt-4">
|
<main class="col px-md-4 pt-4">
|
||||||
{{ if .Flash }}
|
{{ if .Flash }}
|
||||||
<div class="alert alert-info" role="alert">
|
<div class="alert alert-info" role="alert">
|
||||||
{{ .Flash }}
|
{{ .Flash }}
|
||||||
@@ -72,15 +74,13 @@
|
|||||||
{{ template "content" . }}
|
{{ template "content" . }}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="bg-light text-center text-muted py-3 mt-auto border-top">
|
{{ template "footer" . }}
|
||||||
<small>© xxx SynLotto. All rights reserved. | <a href="/privacy">Privacy Policy</a> | <a href="/privacy">Terms & Conditions</a> | <a href="/privacy">Contact Us </a></small>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<!-- JS -->
|
<!-- JS -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
Reference in New Issue
Block a user