Full app, v1.0. Contains web app and cli.
This commit is contained in:
parent
9f310f98db
commit
9f894671b0
42 changed files with 2245 additions and 0 deletions
46
.air.toml
Normal file
46
.air.toml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "areweplaying"
|
||||||
|
cmd = "make build"
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = [".*_templ.go", "_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "templ", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
post_cmd = []
|
||||||
|
pre_cmd = []
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -7,6 +7,16 @@
|
||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
.env
|
||||||
|
areweplaying
|
||||||
|
areweplaying.*
|
||||||
|
awp-cli
|
||||||
|
awp-cli.*
|
||||||
|
players.json
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# air-related stuff
|
||||||
|
tmp
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
|
||||||
17
Makefile
Normal file
17
Makefile
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
all: build
|
||||||
|
|
||||||
|
build:
|
||||||
|
templ generate
|
||||||
|
go build -o areweplaying cmd/web/*.go
|
||||||
|
go build -o awp-cli cmd/cli/*.go
|
||||||
|
|
||||||
|
linuxbuild:
|
||||||
|
templ generate
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o areweplaying.linux cmd/web/*.go
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o awp-cli.linux cmd/cli/*.go
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf areweplaying
|
||||||
|
rm -rf areweplaying.*
|
||||||
|
|
||||||
|
test:
|
||||||
82
cmd/cli/cmd/check.go
Normal file
82
cmd/cli/cmd/check.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/email"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isAttending(body string) string {
|
||||||
|
body = strings.ToLower(body)
|
||||||
|
lines := strings.Fields(body)
|
||||||
|
if strings.Contains(lines[0], "yes") {
|
||||||
|
return "yes"
|
||||||
|
} else if strings.Contains(lines[0], "no") {
|
||||||
|
return "no"
|
||||||
|
} else {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkCmd represents the check command
|
||||||
|
var checkCmd = &cobra.Command{
|
||||||
|
Use: "check",
|
||||||
|
Short: "Checks for new emails on player status",
|
||||||
|
Long: `Checks at culpanvtt@gmail.com for any new
|
||||||
|
emails sent by players to indicate if they
|
||||||
|
will attend the next session.`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
log.Default().Println("Checking for emails")
|
||||||
|
emails, err := email.FetchEmails()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(emails) == 0 {
|
||||||
|
log.Default().Println("No new emails")
|
||||||
|
} else {
|
||||||
|
session, err := data.ReadSessionData()
|
||||||
|
if err != nil {
|
||||||
|
log.Default().Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, email := range emails {
|
||||||
|
if len(email.Body) > 0 {
|
||||||
|
player := session.GetPlayerByEmail(email.FromAddress)
|
||||||
|
if player != nil {
|
||||||
|
attending := isAttending(email.Body)
|
||||||
|
if attending == "yes" || attending == "no" {
|
||||||
|
player.Attending = attending
|
||||||
|
log.Default().Printf("Received player response for %s: %s", player.Name, player.Attending)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.PersistSession(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(checkCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// checkCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// checkCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
||||||
42
cmd/cli/cmd/root.go
Normal file
42
cmd/cli/cmd/root.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "awp-cli",
|
||||||
|
Short: "CLI utility for Are We Playing? web app",
|
||||||
|
Long: `CLI utility for Are We Playing? web app`,
|
||||||
|
// Uncomment the following line if your bare application
|
||||||
|
// has an action associated with it:
|
||||||
|
// Run: func(cmd *cobra.Command, args []string) { },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
// Cobra supports persistent flags, which, if defined here,
|
||||||
|
// will be global for your application.
|
||||||
|
|
||||||
|
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)")
|
||||||
|
|
||||||
|
// Cobra also supports local flags, which will only run
|
||||||
|
// when this action is called directly.
|
||||||
|
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
||||||
27
cmd/cli/main.go
Normal file
27
cmd/cli/main.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hculpan/areweplaying/cmd/cli/cmd"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/utils"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFile := os.Getenv("CLI_LOG_FILE")
|
||||||
|
if len(logFile) > 0 {
|
||||||
|
utils.SetLogFile(logFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
123
cmd/cli/main.go.bak
Normal file
123
cmd/cli/main.go.bak
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/smtp"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/emersion/go-imap"
|
||||||
|
"github.com/emersion/go-imap/client"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var username string
|
||||||
|
var password string
|
||||||
|
var fetchServer string
|
||||||
|
var sendServer string
|
||||||
|
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to load env: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
username = os.Getenv("USERNAME")
|
||||||
|
password = os.Getenv("PASSWORD")
|
||||||
|
fetchServer = os.Getenv("IMAP_SERVER")
|
||||||
|
sendServer = os.Getenv("SMTP_SERVER")
|
||||||
|
port := 993
|
||||||
|
|
||||||
|
if len(username) == 0 || len(password) == 0 || len(fetchServer) == 0 || len(sendServer) == 0 {
|
||||||
|
log.Fatal("missing configuration variables")
|
||||||
|
}
|
||||||
|
|
||||||
|
imapClient, err := connectToServer(username, password, fetchServer, port)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer imapClient.Logout()
|
||||||
|
|
||||||
|
if err := fetchEmails(imapClient); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
to := "harry@culpan.org"
|
||||||
|
subject := "Test Email"
|
||||||
|
body := "This is a test email sent from a Go-based email client."
|
||||||
|
|
||||||
|
if err := sendEmail(username, password, sendServer, 587, to, subject, body); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchEmails(imapClient *client.Client) error {
|
||||||
|
// Select the mailbox you want to read
|
||||||
|
_, err := imapClient.Select("INBOX", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := imap.NewSearchCriteria()
|
||||||
|
criteria.WithoutFlags = []string{"\\Seen"}
|
||||||
|
uids, err := imapClient.Search(criteria)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Search error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(uids) > 0 {
|
||||||
|
// Define the range of emails to fetch
|
||||||
|
seqSet := new(imap.SeqSet)
|
||||||
|
seqSet.AddNum(uids...)
|
||||||
|
|
||||||
|
// Fetch the required message attributes
|
||||||
|
messages := make(chan *imap.Message, 10)
|
||||||
|
section := &imap.BodySectionName{}
|
||||||
|
items := []imap.FetchItem{section.FetchItem(), imap.FetchEnvelope}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := imapClient.Fetch(seqSet, items, messages); err != nil {
|
||||||
|
log.Fatal("Fetch error: " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for msg := range messages {
|
||||||
|
fmt.Println("Subject:", msg.Envelope.Subject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendEmail(username, password, server string, port int, to, subject, body string) error {
|
||||||
|
auth := smtp.PlainAuth("", username, password, server)
|
||||||
|
|
||||||
|
msg := "From: " + username + "\r\n" +
|
||||||
|
"To: " + to + "\r\n" +
|
||||||
|
"Subject: " + subject + "\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
body
|
||||||
|
|
||||||
|
err := smtp.SendMail(server+":"+strconv.Itoa(port), auth, username, []string{to}, []byte(msg))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectToServer(username, password, server string, port int) (*client.Client, error) {
|
||||||
|
c, err := client.DialTLS(fmt.Sprintf("%s:%d", server, port), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Login(username, password); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
68
cmd/web/main.go
Normal file
68
cmd/web/main.go
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/utils"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var appKey []byte
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var port string
|
||||||
|
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
port = "8080"
|
||||||
|
}
|
||||||
|
|
||||||
|
port = os.Getenv("PORT")
|
||||||
|
appKey = []byte(os.Getenv("APP_KEY"))
|
||||||
|
if len(appKey) == 0 {
|
||||||
|
log.Fatal("unable to load APP_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
logFile := os.Getenv("WEB_LOG_FILE")
|
||||||
|
if len(logFile) > 0 {
|
||||||
|
utils.SetLogFile(logFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
workDir, _ := os.Getwd()
|
||||||
|
filesDir := filepath.Join(workDir, "static")
|
||||||
|
FileServer(r, "/static", http.Dir(filesDir))
|
||||||
|
|
||||||
|
routes(r)
|
||||||
|
|
||||||
|
log.Default().Printf("Starting up server on port :%s\n", port)
|
||||||
|
http.ListenAndServe(fmt.Sprintf(":%s", port), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileServer conveniently sets up a http.FileServer handler to serve
|
||||||
|
// static files from a http.FileSystem.
|
||||||
|
func FileServer(r chi.Router, path string, root http.FileSystem) {
|
||||||
|
if strings.ContainsAny(path, "{}*") {
|
||||||
|
panic("FileServer does not permit URL parameters.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if path != "/" && path[len(path)-1] != '/' {
|
||||||
|
r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
|
||||||
|
path += "/"
|
||||||
|
}
|
||||||
|
path += "*"
|
||||||
|
|
||||||
|
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
rctx := chi.RouteContext(r.Context())
|
||||||
|
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
|
||||||
|
fs := http.StripPrefix(pathPrefix, http.FileServer(root))
|
||||||
|
fs.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
156
cmd/web/routes.go
Normal file
156
cmd/web/routes.go
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
"github.com/hculpan/areweplaying/cmd/web/templates"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func routes(r *chi.Mux) {
|
||||||
|
r.Post("/save-setup", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
saveSetupHandler(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
comp := templates.MainPage()
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Post("/setup", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
setupHandler(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/setup", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
setupHandler(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Post("/send-email", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sendEmailHandler(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if ValidateJWTFromCookie(r, appKey) {
|
||||||
|
http.Redirect(w, r, "/setup", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
comp := templates.Login()
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/send-reminder", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sendReminderHandler(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/sessioninfo", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
session, dataErr := data.ReadSessionData()
|
||||||
|
if dataErr != nil {
|
||||||
|
http.Error(w, dataErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
playerName := r.URL.Query().Get("playerName")
|
||||||
|
playerAttending := r.URL.Query().Get("playerAttending")
|
||||||
|
|
||||||
|
if len(playerName) > 0 && len(playerAttending) > 0 {
|
||||||
|
if err := updatePlayerAttending(playerName, playerAttending, session); err != nil {
|
||||||
|
log.Default().Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := templates.PageBody(session)
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateJWTFromCookie(r *http.Request, key []byte) bool {
|
||||||
|
// Retrieve the JWT token from the cookie
|
||||||
|
cookie, err := r.Cookie("jwt_token")
|
||||||
|
if err != nil {
|
||||||
|
// Cookie not found
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString := cookie.Value
|
||||||
|
|
||||||
|
// Parse the token
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
// Validate the algorithm used for the token
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if token is valid
|
||||||
|
if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||||
|
// Here you can also add more checks on claims if needed
|
||||||
|
// For example, check the issuer, subject, or expiration
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateJWTAndStoreInCookie(w http.ResponseWriter, key []byte) error {
|
||||||
|
// Create a new token object, specifying signing method and the claims you would like it to contain.
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||||
|
"exp": time.Now().Add(90 * 24 * time.Hour).Unix(), // 90 days expiration
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sign and get the complete encoded token as a string using the secret
|
||||||
|
tokenString, err := token.SignedString(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a cookie
|
||||||
|
cookie := http.Cookie{
|
||||||
|
Name: "jwt_token",
|
||||||
|
Value: tokenString,
|
||||||
|
Expires: time.Now().Add(90 * 24 * time.Hour),
|
||||||
|
HttpOnly: true, // HttpOnly if you want to prevent access to the cookie from JavaScript
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie
|
||||||
|
http.SetCookie(w, &cookie)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePlayerAttending(name, attending string, session data.Session) error {
|
||||||
|
for i, p := range session.Players {
|
||||||
|
if p.Name == name {
|
||||||
|
switch strings.ToLower(attending) {
|
||||||
|
case "yes", "no":
|
||||||
|
session.Players[i].Attending = strings.ToLower(attending)
|
||||||
|
return data.PersistSession(session)
|
||||||
|
case "toggle":
|
||||||
|
newAttending := session.Players[i].Attending
|
||||||
|
switch newAttending {
|
||||||
|
case "unknown", "no":
|
||||||
|
newAttending = "yes"
|
||||||
|
case "yes":
|
||||||
|
newAttending = "no"
|
||||||
|
}
|
||||||
|
session.Players[i].Attending = newAttending
|
||||||
|
return data.PersistSession(session)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unrecognized attending indicator %q", attending)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("player %q not found", name)
|
||||||
|
}
|
||||||
67
cmd/web/savesetuphandler.go
Normal file
67
cmd/web/savesetuphandler.go
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hculpan/areweplaying/cmd/web/templates"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func saveSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.ParseForm()
|
||||||
|
nextSession := r.FormValue("nextSession")
|
||||||
|
incDays := r.FormValue("incDays")
|
||||||
|
sessionStatus := r.FormValue("sessionStatus")
|
||||||
|
log.Default().Printf("sessionStatus = %s", sessionStatus)
|
||||||
|
|
||||||
|
session, dataErr := data.ReadSessionData()
|
||||||
|
if dataErr != nil {
|
||||||
|
http.Error(w, dataErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := "Save successful!"
|
||||||
|
err := updateSession(session, incDays, nextSession, sessionStatus)
|
||||||
|
if err != nil {
|
||||||
|
msg = fmt.Sprintf("Error in update: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := templates.SaveSetup(msg)
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSession(session data.Session, incDays, nextSession, sessionStatus string) error {
|
||||||
|
if val, err := strconv.Atoi(incDays); err == nil {
|
||||||
|
session.IncrementDays = val
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextSession == "nextSession" {
|
||||||
|
if d, err := time.Parse(data.DATE_FORMAT, session.Date); err == nil {
|
||||||
|
d = d.AddDate(0, 0, session.IncrementDays)
|
||||||
|
session.Date = d.Format(data.DATE_FORMAT)
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range session.Players {
|
||||||
|
if p.Gm {
|
||||||
|
session.Players[i].Attending = "yes"
|
||||||
|
} else {
|
||||||
|
session.Players[i].Attending = "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sessionStatus) > 0 {
|
||||||
|
session.Status = sessionStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.PersistSession(session)
|
||||||
|
}
|
||||||
38
cmd/web/sendemailhandler.go
Normal file
38
cmd/web/sendemailhandler.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hculpan/areweplaying/cmd/web/templates"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/email"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sendEmailHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !ValidateJWTFromCookie(r, appKey) {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ParseForm()
|
||||||
|
to := r.FormValue("to")
|
||||||
|
from := "culpanvtt@gmail.com"
|
||||||
|
subject := r.FormValue("subject")
|
||||||
|
body := r.FormValue("text")
|
||||||
|
toArray := strings.Split(to, ",")
|
||||||
|
for i := range toArray {
|
||||||
|
toArray[i] = strings.Trim(toArray[i], " \t\n\r")
|
||||||
|
}
|
||||||
|
e := email.NewEmail(toArray, from, subject, body)
|
||||||
|
|
||||||
|
// log.Default().Printf("got email: To: %s, From: %s, Subject: %q, Body: %q", to, from, subject, body)
|
||||||
|
err := email.SendEmail(e)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := templates.SendEmail("Email sent!", "")
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
}
|
||||||
49
cmd/web/sendreminderhandler.go
Normal file
49
cmd/web/sendreminderhandler.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hculpan/areweplaying/cmd/web/templates"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sendReminderHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !ValidateJWTFromCookie(r, appKey) {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := data.ReadSessionData()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
to := buildToList(session)
|
||||||
|
subject := fmt.Sprintf("Game Reminder for %s", session.Date)
|
||||||
|
text := fmt.Sprintf(
|
||||||
|
`This is a game reminder for %s. Please respond with a "yes" or "no" to indicate
|
||||||
|
if you plan to attend.
|
||||||
|
|
||||||
|
If you wish to check on the status of the game, you may do so at https://awp.culpanvtt.org.
|
||||||
|
|
||||||
|
Thank you,
|
||||||
|
Your friendly "Are We Playing?" automated system`, session.Date)
|
||||||
|
|
||||||
|
comp := templates.SendReminder(to, subject, text)
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildToList(session data.Session) string {
|
||||||
|
emails := []string{}
|
||||||
|
for _, player := range session.Players {
|
||||||
|
if len(player.Email) > 0 {
|
||||||
|
emails = append(emails, player.Email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(emails, ", ")
|
||||||
|
}
|
||||||
31
cmd/web/setuphandler.go
Normal file
31
cmd/web/setuphandler.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/hculpan/areweplaying/cmd/web/templates"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
session, dataErr := data.ReadSessionData()
|
||||||
|
if dataErr != nil {
|
||||||
|
http.Error(w, dataErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ValidateJWTFromCookie(r, appKey) {
|
||||||
|
r.ParseForm()
|
||||||
|
pword := r.FormValue("password")
|
||||||
|
if len(pword) == 0 || pword != session.Password {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateJWTAndStoreInCookie(w, appKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := templates.Setup(session)
|
||||||
|
comp.Render(context.Background(), w)
|
||||||
|
}
|
||||||
14
cmd/web/templates/header.templ
Normal file
14
cmd/web/templates/header.templ
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
templ Header() {
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Are We Playing?</title>
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"
|
||||||
|
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
}
|
||||||
53
cmd/web/templates/header_templ.go
Normal file
53
cmd/web/templates/header_templ.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func Header() templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<head><title>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Are We Playing?`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title><link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC\" crossorigin=\"anonymous\"><script src=\"https://unpkg.com/htmx.org@1.9.10\" integrity=\"sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC\" crossorigin=\"anonymous\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var3 := ``
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script></head>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
33
cmd/web/templates/index.templ
Normal file
33
cmd/web/templates/index.templ
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
templ MainPage() {
|
||||||
|
<html>
|
||||||
|
|
||||||
|
@Header()
|
||||||
|
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="container mt-4" id="session-info" hx-get="/sessioninfo" hx-swap="innerHTML" hx-trigger="load"
|
||||||
|
hx-target="#session-info">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<a href="/login">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
|
class="bi bi-gear text-secondary" viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0" />
|
||||||
|
<path
|
||||||
|
d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src=" https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous">
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
53
cmd/web/templates/index_templ.go
Normal file
53
cmd/web/templates/index_templ.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func MainPage() templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"bg-dark text-light\"><div class=\"container mt-4\" id=\"session-info\" hx-get=\"/sessioninfo\" hx-swap=\"innerHTML\" hx-trigger=\"load\" hx-target=\"#session-info\"></div><div class=\"container\"><div class=\"d-flex justify-content-end\"><a href=\"/login\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-gear text-secondary\" viewBox=\"0 0 16 16\"><path d=\"M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0\"></path> <path d=\"M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115z\"></path></svg></a></div></div><script src=\" https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js\" integrity=\"sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM\" crossorigin=\"anonymous\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `
|
||||||
|
`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
31
cmd/web/templates/login.templ
Normal file
31
cmd/web/templates/login.templ
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
templ Login() {
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
@Header()
|
||||||
|
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row d-flex justify-content-center">
|
||||||
|
<div class="col-4">
|
||||||
|
<form action="/setup" method="POST">
|
||||||
|
<h1 class="h3 mb-3 mt-4 fw-normal">Please sign in</h1>
|
||||||
|
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="password" class="form-control mb-2" id="password" name="password"
|
||||||
|
placeholder="Password" />
|
||||||
|
<label for="floatingPassword">Password</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
}
|
||||||
70
cmd/web/templates/login_templ.go
Normal file
70
cmd/web/templates/login_templ.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func Login() templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"bg-dark text-light\"><div class=\"container\"><div class=\"row d-flex justify-content-center\"><div class=\"col-4\"><form action=\"/setup\" method=\"POST\"><h1 class=\"h3 mb-3 mt-4 fw-normal\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Please sign in`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><div class=\"form-floating\"><input type=\"password\" class=\"form-control mb-2\" id=\"password\" name=\"password\" placeholder=\"Password\"> <label for=\"floatingPassword\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var3 := `Password`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label></div><button class=\"w-100 btn btn-lg btn-primary\" type=\"submit\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var4 := `Sign in`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></form></div></div></div></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
50
cmd/web/templates/pagebody.templ
Normal file
50
cmd/web/templates/pagebody.templ
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ PageBody(session data.Session) {
|
||||||
|
<div class="row">
|
||||||
|
<div class="col h1 text-center">Next Session: { session.Date }</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center mt-4">
|
||||||
|
<div class="col col-lg-3">
|
||||||
|
<table class="table table-dark table-striped border border-secondary">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="col-1 text-center">Attending</th>
|
||||||
|
<th scope="col" class="col-2 text-center">Player</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
for _, player := range session.Players {
|
||||||
|
<tr>
|
||||||
|
<td class="col col-lg-1 align-middle text-center" hx-get={ player.ToggleUrl } hx-swap="innerHTML"
|
||||||
|
hx-target="#session-info" hx-trigger="click">
|
||||||
|
if player.Attending == "yes" {
|
||||||
|
<img src="/static/check_green.png" width="30" height="30" />
|
||||||
|
} else if player.Attending == "no"{
|
||||||
|
<img src="/static/check_red.png" width="30" height="30" />
|
||||||
|
} else {
|
||||||
|
<img src="/static/check_grey.png" width="30" height="30" />
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td class="col col-lg-2 align-middle text-center" style="height: 50px;">{ player.Name }</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
if session.Status == "planned" {
|
||||||
|
<div class="col h1 text-center text-success">Status: { session.Status }</div>
|
||||||
|
} else if session.Status == "canceled" {
|
||||||
|
<div class="col h1 text-center text-danger">Status: { session.Status }</div>
|
||||||
|
} else {
|
||||||
|
<div class="col h1 text-center">Status: { session.Status }</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
200
cmd/web/templates/pagebody_templ.go
Normal file
200
cmd/web/templates/pagebody_templ.go
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PageBody(session data.Session) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"row\"><div class=\"col h1 text-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Next Session: `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(session.Date)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/pagebody.templ`, Line: 8, Col: 64}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div><div class=\"row justify-content-center mt-4\"><div class=\"col col-lg-3\"><table class=\"table table-dark table-striped border border-secondary\"><thead><tr><th scope=\"col\" class=\"col-1 text-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var4 := `Attending`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</th><th scope=\"col\" class=\"col-2 text-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var5 := `Player`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</th></tr></thead> <tbody>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, player := range session.Players {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td class=\"col col-lg-1 align-middle text-center\" hx-get=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(player.ToggleUrl))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"innerHTML\" hx-target=\"#session-info\" hx-trigger=\"click\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if player.Attending == "yes" {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<img src=\"/static/check_green.png\" width=\"30\" height=\"30\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else if player.Attending == "no" {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<img src=\"/static/check_red.png\" width=\"30\" height=\"30\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<img src=\"/static/check_grey.png\" width=\"30\" height=\"30\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"col col-lg-2 align-middle text-center\" style=\"height: 50px;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(player.Name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/pagebody.templ`, Line: 32, Col: 105}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div></div><div class=\"row\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if session.Status == "planned" {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col h1 text-center text-success\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var7 := `Status: `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(session.Status)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/pagebody.templ`, Line: 41, Col: 73}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else if session.Status == "canceled" {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col h1 text-center text-danger\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var9 := `Status: `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(session.Status)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/pagebody.templ`, Line: 43, Col: 72}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col h1 text-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var11 := `Status: `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var12 string
|
||||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(session.Status)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/pagebody.templ`, Line: 45, Col: 60}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
28
cmd/web/templates/save_setup.templ
Normal file
28
cmd/web/templates/save_setup.templ
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
templ SaveSetup(status string) {
|
||||||
|
<html>
|
||||||
|
@Header()
|
||||||
|
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
<a href="/" class="btn btn-secondary"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||||
|
fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8" />
|
||||||
|
</svg>Back to Main Page</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h1>{ status }</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class=" container">
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
65
cmd/web/templates/save_setup_templ.go
Normal file
65
cmd/web/templates/save_setup_templ.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func SaveSetup(status string) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"bg-dark text-light\"><div class=\"container\"><div class=\"row\"><div class=\"col-3\"><a href=\"/\" class=\"btn btn-secondary\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"></path></svg>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Back to Main Page`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div></div><div class=\"row mt-4\"><h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(status)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/save_setup.templ`, Line: 18, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1></div></div><footer class=\" container\"></footer></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
31
cmd/web/templates/sendemail.templ
Normal file
31
cmd/web/templates/sendemail.templ
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
templ SendEmail(status string, msg string) {
|
||||||
|
<html>
|
||||||
|
@Header()
|
||||||
|
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
<a href="/" class="btn btn-secondary"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||||
|
fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8" />
|
||||||
|
</svg>Back to Main Page</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h1>{ status }</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<p>{ msg }</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class=" container">
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
78
cmd/web/templates/sendemail_templ.go
Normal file
78
cmd/web/templates/sendemail_templ.go
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func SendEmail(status string, msg string) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"bg-dark text-light\"><div class=\"container\"><div class=\"row\"><div class=\"col-3\"><a href=\"/\" class=\"btn btn-secondary\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"></path></svg>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Back to Main Page`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div></div><div class=\"row mt-4\"><h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(status)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/sendemail.templ`, Line: 18, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1></div><div class=\"row\"><p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(msg)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/sendemail.templ`, Line: 21, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p></div></div><footer class=\" container\"></footer></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
47
cmd/web/templates/sendreminder.templ
Normal file
47
cmd/web/templates/sendreminder.templ
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
|
||||||
|
templ SendReminder(to string, subject string, text string) {
|
||||||
|
<html>
|
||||||
|
@Header()
|
||||||
|
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
<a href="/" class="btn btn-secondary"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||||
|
fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8" />
|
||||||
|
</svg>Back to Main Page</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h1>Send Reminder</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<form action="/send-email" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="toControl" class="form-label">To</label>
|
||||||
|
<input type="text" id="toControl" name="to" value={to} />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="subjectControl" class="form-label">Subject</label>
|
||||||
|
<input type="text" class="form-control" id="subjectControl" name="subject" value={ subject } />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="textControl" class="form-label">Text to send</label>
|
||||||
|
<textarea class="form-control" id="subjectControl" name="text" rows="5">{ text }</textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Send Reminder</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class=" container">
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
}
|
||||||
126
cmd/web/templates/sendreminder_templ.go
Normal file
126
cmd/web/templates/sendreminder_templ.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func SendReminder(to string, subject string, text string) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"bg-dark text-light\"><div class=\"container\"><div class=\"row\"><div class=\"col-3\"><a href=\"/\" class=\"btn btn-secondary\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"></path></svg>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Back to Main Page`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div></div><div class=\"row mt-4\"><h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var3 := `Send Reminder`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1></div><div class=\"row\"><form action=\"/send-email\" method=\"post\"><div class=\"mb-3\"><label for=\"toControl\" class=\"form-label\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var4 := `To`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <input type=\"text\" id=\"toControl\" name=\"to\" value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(to))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></div><div class=\"mb-3\"><label for=\"subjectControl\" class=\"form-label\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var5 := `Subject`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <input type=\"text\" class=\"form-control\" id=\"subjectControl\" name=\"subject\" value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(subject))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></div><div class=\"mb-3\"><label for=\"textControl\" class=\"form-label\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var6 := `Text to send`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <textarea class=\"form-control\" id=\"subjectControl\" name=\"text\" rows=\"5\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/web/templates/sendreminder.templ`, Line: 33, Col: 98}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</textarea></div><button type=\"submit\" class=\"btn btn-primary\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var8 := `Send Reminder`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></form></div></div><footer class=\" container\"></footer></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
73
cmd/web/templates/setup.templ
Normal file
73
cmd/web/templates/setup.templ
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ Setup(session data.Session) {
|
||||||
|
<html>
|
||||||
|
@Header()
|
||||||
|
|
||||||
|
<body class="bg-dark text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
<a href="/" class="btn btn-secondary"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||||
|
fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8" />
|
||||||
|
</svg>Back to Main Page</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<a href="/send-reminder" class="btn btn-secondary">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
|
class="bi bi-envelope-plus" viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M2 2a2 2 0 0 0-2 2v8.01A2 2 0 0 0 2 14h5.5a.5.5 0 0 0 0-1H2a1 1 0 0 1-.966-.741l5.64-3.471L8 9.583l7-4.2V8.5a.5.5 0 0 0 1 0V4a2 2 0 0 0-2-2zm3.708 6.208L1 11.105V5.383zM1 4.217V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v.217l-7 4.2z" />
|
||||||
|
<path
|
||||||
|
d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0m-3.5-2a.5.5 0 0 0-.5.5v1h-1a.5.5 0 0 0 0 1h1v1a.5.5 0 0 0 1 0v-1h1a.5.5 0 0 0 0-1h-1v-1a.5.5 0 0 0-.5-.5" />
|
||||||
|
</svg>
|
||||||
|
Send Reminder
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-4">
|
||||||
|
<h1>Setup</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<form action="/save-setup" method="post">
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" value="nextSession" id="nextSession"
|
||||||
|
name="nextSession" />
|
||||||
|
<label class="form-check-label" for="nextSession">Change to next session</label>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="incrementDaysInput" class="form-label">Days to Increment</label>
|
||||||
|
<input type="number" class="form-control" id="incrementDaysInput" name="incDays" min="1"
|
||||||
|
style="max-width: 150px;" max="999" value={ strconv.Itoa(session.IncrementDays) } />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="sessionStatus" class="form-label">Session Status</label>
|
||||||
|
<select class="form-select" aria-label="Default select example" name="sessionStatus"
|
||||||
|
style="max-width: 150px;">
|
||||||
|
<option selected></option>
|
||||||
|
<option value="planned">Planned</option>
|
||||||
|
<option value="canceled">Canceled</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" value="sendEmail" id="sendEmail" name="sendEmail" />
|
||||||
|
<label class="form-check-label" for="sendEmail">Send Email</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class=" container">
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
146
cmd/web/templates/setup_templ.go
Normal file
146
cmd/web/templates/setup_templ.go
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.513
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hculpan/areweplaying/pkg/data"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup(session data.Session) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"bg-dark text-light\"><div class=\"container\"><div class=\"row\"><div class=\"col-3\"><a href=\"/\" class=\"btn btn-secondary\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"></path></svg>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var2 := `Back to Main Page`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"col-3\"><a href=\"/send-reminder\" class=\"btn btn-secondary\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-envelope-plus\" viewBox=\"0 0 16 16\"><path d=\"M2 2a2 2 0 0 0-2 2v8.01A2 2 0 0 0 2 14h5.5a.5.5 0 0 0 0-1H2a1 1 0 0 1-.966-.741l5.64-3.471L8 9.583l7-4.2V8.5a.5.5 0 0 0 1 0V4a2 2 0 0 0-2-2zm3.708 6.208L1 11.105V5.383zM1 4.217V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v.217l-7 4.2z\"></path> <path d=\"M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0m-3.5-2a.5.5 0 0 0-.5.5v1h-1a.5.5 0 0 0 0 1h1v1a.5.5 0 0 0 1 0v-1h1a.5.5 0 0 0 0-1h-1v-1a.5.5 0 0 0-.5-.5\"></path></svg> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var3 := `Send Reminder`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div></div><div class=\"row mt-4\"><h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var4 := `Setup`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1></div><div class=\"row\"><form action=\"/save-setup\" method=\"post\"><div class=\"form-check mb-3\"><input class=\"form-check-input\" type=\"checkbox\" value=\"nextSession\" id=\"nextSession\" name=\"nextSession\"> <label class=\"form-check-label\" for=\"nextSession\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var5 := `Change to next session`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label></div><div class=\"mb-3\"><label for=\"incrementDaysInput\" class=\"form-label\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var6 := `Days to Increment`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <input type=\"number\" class=\"form-control\" id=\"incrementDaysInput\" name=\"incDays\" min=\"1\" style=\"max-width: 150px;\" max=\"999\" value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(strconv.Itoa(session.IncrementDays)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></div><div class=\"mb-3\"><label for=\"sessionStatus\" class=\"form-label\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var7 := `Session Status`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <select class=\"form-select\" aria-label=\"Default select example\" name=\"sessionStatus\" style=\"max-width: 150px;\"><option selected></option> <option value=\"planned\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var8 := `Planned`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option> <option value=\"canceled\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var9 := `Canceled`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option></select></div><div class=\"form-check mb-3\"><input class=\"form-check-input\" type=\"checkbox\" value=\"sendEmail\" id=\"sendEmail\" name=\"sendEmail\"> <label class=\"form-check-label\" for=\"sendEmail\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var10 := `Send Email`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label></div><button type=\"submit\" class=\"btn btn-primary\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var11 := `Save`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></form></div></div><footer class=\" container\"></footer></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
16
exmaple_players.json
Normal file
16
exmaple_players.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"date": "Tuesday Jan 23, 2024 6:30pm",
|
||||||
|
"players": [
|
||||||
|
{
|
||||||
|
"name": "Player1",
|
||||||
|
"attending": "unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Player2",
|
||||||
|
"attending": "yes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inc_days": 7,
|
||||||
|
"status": "planned",
|
||||||
|
"password": "ABC123"
|
||||||
|
}
|
||||||
22
go.mod
Normal file
22
go.mod
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
module github.com/hculpan/areweplaying
|
||||||
|
|
||||||
|
go 1.21.6
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/a-h/templ v0.2.513
|
||||||
|
github.com/go-chi/chi/v5 v5.0.11
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/emersion/go-imap v1.2.1
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/spf13/cobra v1.8.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
)
|
||||||
30
go.sum
Normal file
30
go.sum
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
github.com/a-h/templ v0.2.513 h1:ZmwGAOx4NYllnHy+FTpusc4+c5msoMpPIYX0Oy3dNqw=
|
||||||
|
github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
||||||
|
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||||
|
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||||
|
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
31
pkg/data/data.go
Normal file
31
pkg/data/data.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package data
|
||||||
|
|
||||||
|
const (
|
||||||
|
DATE_FORMAT string = "Monday Jan 2, 2006 3:04pm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Attending string `json:"attending"`
|
||||||
|
ToggleUrl string `json:"-"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Gm bool `json:"gm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
Date string `json:"date"`
|
||||||
|
Players []Player `json:"players"`
|
||||||
|
IncrementDays int `json:"inc_days"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) GetPlayerByEmail(addr string) *Player {
|
||||||
|
for i, p := range s.Players {
|
||||||
|
if addr == p.Email {
|
||||||
|
return &s.Players[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
35
pkg/data/loaddata.go
Normal file
35
pkg/data/loaddata.go
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadSessionData() (Session, error) {
|
||||||
|
var session Session
|
||||||
|
data, err := os.ReadFile("players.json")
|
||||||
|
if err != nil {
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(data, &session)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
for i, p := range session.Players {
|
||||||
|
session.Players[i].ToggleUrl = "/sessioninfo?playerName=" + p.Name + "&playerAttending=toggle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func PersistSession(session Session) error {
|
||||||
|
// Convert the object to JSON
|
||||||
|
jsonData, err := json.MarshalIndent(session, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write JSON data to file
|
||||||
|
err = os.WriteFile("players.json", jsonData, 0644)
|
||||||
|
return err
|
||||||
|
}
|
||||||
75
pkg/email/config.go
Normal file
75
pkg/email/config.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
package email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/emersion/go-imap/client"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAP_SERVICE EmailService = 993
|
||||||
|
SMTP_SERVICE EmailService = 587
|
||||||
|
)
|
||||||
|
|
||||||
|
type EmailService int
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
ImapServer string
|
||||||
|
SmtpServer string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig(username, password, imapServer, smtpServer string) *Config {
|
||||||
|
return &Config{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
ImapServer: imapServer,
|
||||||
|
SmtpServer: smtpServer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigFromEnv() (*Config, error) {
|
||||||
|
var username string
|
||||||
|
var password string
|
||||||
|
var fetchServer string
|
||||||
|
var sendServer string
|
||||||
|
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error loading .env file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
username = os.Getenv("USERNAME")
|
||||||
|
password = os.Getenv("PASSWORD")
|
||||||
|
fetchServer = os.Getenv("IMAP_SERVER")
|
||||||
|
sendServer = os.Getenv("SMTP_SERVER")
|
||||||
|
|
||||||
|
if len(username) == 0 || len(password) == 0 || len(fetchServer) == 0 || len(sendServer) == 0 {
|
||||||
|
return nil, fmt.Errorf("missing configuration variables")
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewConfig(username, password, fetchServer, sendServer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) ConnectToServer(service EmailService) (*client.Client, error) {
|
||||||
|
port := int(service)
|
||||||
|
var host string
|
||||||
|
if service == SMTP_SERVICE {
|
||||||
|
host = c.SmtpServer
|
||||||
|
} else {
|
||||||
|
host = c.ImapServer
|
||||||
|
}
|
||||||
|
client, err := client.DialTLS(fmt.Sprintf("%s:%d", host, port), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Login(c.Username, c.Password); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
27
pkg/email/email.go
Normal file
27
pkg/email/email.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package email
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Email struct {
|
||||||
|
ToAddress []string
|
||||||
|
FromAddress string
|
||||||
|
Subject string
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEmail(to []string, from, subject, body string) *Email {
|
||||||
|
return &Email{
|
||||||
|
ToAddress: to,
|
||||||
|
FromAddress: from,
|
||||||
|
Subject: subject,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Email) String() string {
|
||||||
|
bodyLength := len(e.Body)
|
||||||
|
if bodyLength > 10 {
|
||||||
|
bodyLength = 10
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("To: %s, From: %s, Subject: %s, Body: %s", e.ToAddress, e.FromAddress, e.Subject, e.Body[:bodyLength])
|
||||||
|
}
|
||||||
100
pkg/email/fetch.go
Normal file
100
pkg/email/fetch.go
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
package email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/mail"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/emersion/go-imap"
|
||||||
|
"github.com/emersion/go-imap/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FetchEmails() ([]Email, error) {
|
||||||
|
config, err := NewConfigFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
imapClient, err := config.ConnectToServer(IMAP_SERVICE)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer imapClient.Logout()
|
||||||
|
|
||||||
|
return fetchEmails(imapClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func concatAddresses(addresses []*imap.Address) string {
|
||||||
|
result := []string{}
|
||||||
|
|
||||||
|
for _, address := range addresses {
|
||||||
|
if len(address.PersonalName) > 0 {
|
||||||
|
result = append(result, fmt.Sprintf("%s <%s>", address.PersonalName, address.Address()))
|
||||||
|
} else {
|
||||||
|
result = append(result, address.Address())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(result, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchEmails(imapClient *client.Client) ([]Email, error) {
|
||||||
|
result := []Email{}
|
||||||
|
|
||||||
|
// Select the mailbox you want to read
|
||||||
|
_, err := imapClient.Select("INBOX", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := imap.NewSearchCriteria()
|
||||||
|
criteria.WithoutFlags = []string{imap.SeenFlag}
|
||||||
|
uids, err := imapClient.Search(criteria)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Search error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(uids) > 0 {
|
||||||
|
// Define the range of emails to fetch
|
||||||
|
seqSet := new(imap.SeqSet)
|
||||||
|
seqSet.AddNum(uids...)
|
||||||
|
|
||||||
|
// Fetch the required message attributes
|
||||||
|
messages := make(chan *imap.Message, 10)
|
||||||
|
section := &imap.BodySectionName{}
|
||||||
|
items := []imap.FetchItem{section.FetchItem(), imap.FetchEnvelope}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := imapClient.Fetch(seqSet, items, messages); err != nil {
|
||||||
|
log.Fatal("Fetch error: " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for msg := range messages {
|
||||||
|
toAddress := concatAddresses(msg.Envelope.To)
|
||||||
|
fromAddress := msg.Envelope.From[0].Address()
|
||||||
|
r := msg.GetBody(section)
|
||||||
|
if r == nil {
|
||||||
|
return result, fmt.Errorf("server didn't returned message body")
|
||||||
|
}
|
||||||
|
m, err := mail.ReadMessage(r)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
body, err := io.ReadAll(m.Body)
|
||||||
|
|
||||||
|
email := NewEmail(
|
||||||
|
[]string{toAddress},
|
||||||
|
fromAddress,
|
||||||
|
msg.Envelope.Subject,
|
||||||
|
string(body),
|
||||||
|
)
|
||||||
|
result = append(result, *email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
37
pkg/email/send.go
Normal file
37
pkg/email/send.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/smtp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SendEmail(email *Email) error {
|
||||||
|
config, err := NewConfigFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = sendEmail(config.Username, config.Password, config.SmtpServer, int(SMTP_SERVICE), email.ToAddress, email.Subject, email.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendEmail(username, password, server string, port int, to []string, subject, body string) error {
|
||||||
|
auth := smtp.PlainAuth("", username, password, server)
|
||||||
|
|
||||||
|
msg := "From: " + username + "\r\n" +
|
||||||
|
"To: " + strings.Join(to, ",") + "\r\n" +
|
||||||
|
"Subject: " + subject + "\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
body
|
||||||
|
|
||||||
|
err := smtp.SendMail(server+":"+strconv.Itoa(port), auth, username, to, []byte(msg))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
18
pkg/utils/utils.go
Normal file
18
pkg/utils/utils.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetLogFile(path string) error {
|
||||||
|
logFile, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetOutput(logFile)
|
||||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
BIN
static/check_green.png
Normal file
BIN
static/check_green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
BIN
static/check_grey.png
Normal file
BIN
static/check_grey.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
static/check_red.png
Normal file
BIN
static/check_red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Loading…
Reference in a new issue