mirror of
https://github.com/ION606/MailPocket.git
synced 2026-05-14 22:06:55 +00:00
added admin routes
This commit is contained in:
+172
@@ -0,0 +1,172 @@
|
|||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
adminOnce sync.Once
|
||||||
|
adminServer *http.Server
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdminServer struct {
|
||||||
|
DB *sql.DB
|
||||||
|
auth *AdminAuth
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdminAuth struct {
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdminAuth) Middleware(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
provided := r.Header.Get("X-Admin-Password")
|
||||||
|
if provided != a.Password {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartAdminServer(dbdir string) {
|
||||||
|
var port, _, _ = GetArgs()
|
||||||
|
|
||||||
|
adminOnce.Do(func() {
|
||||||
|
adminPassword := os.Getenv("ADMIN_PASSWORD")
|
||||||
|
if adminPassword == "" {
|
||||||
|
log.Fatal("ADMIN_PASSWORD environment variable required")
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite", filepath.Join(dbdir, "admin.db"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to open admin database:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
createTables(db)
|
||||||
|
|
||||||
|
as := &AdminServer{
|
||||||
|
DB: db,
|
||||||
|
auth: &AdminAuth{Password: adminPassword},
|
||||||
|
}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/forms/", as.handleForms)
|
||||||
|
mux.HandleFunc("/submit/", as.handleSubmit)
|
||||||
|
|
||||||
|
adminServer = &http.Server{
|
||||||
|
Addr: ":" + port,
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Starting admin server on port", port)
|
||||||
|
go func() {
|
||||||
|
if err := adminServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Fatal("Admin server failed:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTables(db *sql.DB) {
|
||||||
|
db.Exec(`CREATE TABLE IF NOT EXISTS forms (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)`)
|
||||||
|
|
||||||
|
db.Exec(`CREATE TABLE IF NOT EXISTS submissions (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
form_name TEXT,
|
||||||
|
data TEXT,
|
||||||
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY(form_name) REFERENCES forms(name) ON DELETE CASCADE
|
||||||
|
)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AdminServer) handleForms(w http.ResponseWriter, r *http.Request) {
|
||||||
|
formName := strings.TrimPrefix(r.URL.Path, "/forms/")
|
||||||
|
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodPost:
|
||||||
|
as.auth.Middleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, err := as.DB.Exec("INSERT INTO forms(name) VALUES (?)", formName)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Form creation failed", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
})(w, r)
|
||||||
|
|
||||||
|
case http.MethodDelete:
|
||||||
|
as.auth.Middleware(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, err := as.DB.Exec("DELETE FROM forms WHERE name = ?", formName)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Form deletion failed", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
})(w, r)
|
||||||
|
|
||||||
|
case http.MethodGet:
|
||||||
|
var exists bool
|
||||||
|
err := as.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM forms WHERE name = ?)", formName).Scan(&exists)
|
||||||
|
if err != nil || !exists {
|
||||||
|
http.Error(w, "Form not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := as.DB.Query("SELECT data FROM submissions WHERE form_name = ?", formName)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to retrieve submissions", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var submissions []string
|
||||||
|
for rows.Next() {
|
||||||
|
var data string
|
||||||
|
if err := rows.Scan(&data); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
submissions = append(submissions, data)
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(submissions)
|
||||||
|
|
||||||
|
default:
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AdminServer) handleSubmit(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
formName := strings.TrimPrefix(r.URL.Path, "/submit/")
|
||||||
|
data := r.FormValue("data")
|
||||||
|
|
||||||
|
var exists bool
|
||||||
|
err := as.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM forms WHERE name = ?)", formName).Scan(&exists)
|
||||||
|
if err != nil || !exists {
|
||||||
|
http.Error(w, "Form does not exist", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = as.DB.Exec(`INSERT INTO submissions(form_name, data) VALUES (?, ?)`, formName, data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Submission failed", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
}
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
module shared
|
module shared
|
||||||
|
|
||||||
go 1.23.6
|
go 1.23.6
|
||||||
|
|
||||||
|
require modernc.org/sqlite v1.36.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect
|
||||||
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
modernc.org/libc v1.61.13 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.8.2 // indirect
|
||||||
|
)
|
||||||
|
|||||||
+15
-6
@@ -5,15 +5,24 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GetArgs() (string, string, string) {
|
||||||
func GetArgs() (string, string) {
|
|
||||||
var PORT string
|
var PORT string
|
||||||
if len(os.Args) > 1 {
|
var ADMINPORT string
|
||||||
|
if len(os.Args) > 2 {
|
||||||
|
ADMINPORT = os.Args[2]
|
||||||
PORT = os.Args[1]
|
PORT = os.Args[1]
|
||||||
} else {
|
} else if len(os.Args) > 1 {
|
||||||
|
PORT = os.Args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if PORT == "" {
|
||||||
PORT = "15521"
|
PORT = "15521"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ADMINPORT == "" {
|
||||||
|
ADMINPORT = "15522"
|
||||||
|
}
|
||||||
|
|
||||||
dbdir := "data"
|
dbdir := "data"
|
||||||
|
|
||||||
isDocker := os.Getenv("container") == "docker" || os.Getenv("DOCKER") == "true" || func() bool { _, err := os.Stat("/.dockerenv"); return err == nil }()
|
isDocker := os.Getenv("container") == "docker" || os.Getenv("DOCKER") == "true" || func() bool { _, err := os.Stat("/.dockerenv"); return err == nil }()
|
||||||
@@ -27,5 +36,5 @@ func GetArgs() (string, string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PORT, dbdir
|
return ADMINPORT, PORT, dbdir
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user