Speichere Konfiguration in config/config.json

This commit is contained in:
Gonne 2022-09-19 14:46:16 +02:00
parent 43b3631da2
commit c38286bcc5
14 changed files with 249 additions and 78 deletions

88
config/config.go Normal file
View file

@ -0,0 +1,88 @@
package config
import (
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
)
type Config struct {
Server struct {
ListenAddress string
ListenPort int
Protocol string
Domain string
}
Date struct {
MinuteGranularity int
}
Request struct {
SecretLength int
}
Mailer struct {
Type string
FromAddress string
FromName template.HTML
SmtpHost string
SmtpPort int
SmtpUseAuth bool
SmtpIdentity string
SmtpPassword string
}
SQL struct {
Type string
SQLiteFile string
MysqlUser string
MysqlPassword string
MysqlHost string
MysqlPort int
MysqlDatabase string
}
}
// ReadConfigFile takes a file path as an argument and attempts to
// unmarshal the content of the file into a struct containing a
// configuration.
func ReadConfigFile(filename string, conf *Config) error {
configData, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
err = json.Unmarshal(configData, conf)
if err != nil {
return err
}
return validateConfig(conf)
}
func validateConfig(conf *Config) error {
var err error
if !(conf.Server.ListenPort >= 1 && conf.Server.ListenPort <= 65535) {
err = fmt.Errorf("Validating config: Server port must be between 1 and 65535, but is %d.", conf.Server.ListenPort)
log.Println(err.Error())
}
if !(conf.Server.Protocol == "http" || conf.Server.Protocol == "https") {
err = fmt.Errorf("Validating config: Server protocol must be http or https, but is '%s'.", conf.Server.Protocol)
log.Println(err.Error())
}
if !(conf.Date.MinuteGranularity >= 1 && conf.Date.MinuteGranularity <= 60) {
err = fmt.Errorf("Validating config: Minute granularity must be between 1 and 60, but is %d.", conf.Date.MinuteGranularity)
log.Println(err.Error())
}
if !(conf.Request.SecretLength >= 5 && conf.Request.SecretLength <= 50) {
err = fmt.Errorf("Validating config: Requests' secret length must be between 5 and 50, but is %d.", conf.Request.SecretLength)
log.Println(err.Error())
}
if !(conf.Mailer.Type == "Stdout" || conf.Mailer.Type == "Smtp") {
err = fmt.Errorf("Validating config: Mailer type must be 'stdout' or 'smtp', but is '%s'.", conf.Mailer.Type)
log.Println(err.Error())
}
if !(conf.SQL.Type == "SQLite" || conf.SQL.Type == "Mysql") {
err = fmt.Errorf("Validating config: SQL type must be 'SQLite' or 'Mysql', but is '%s'.", conf.SQL.Type)
log.Println(err.Error())
}
return err
}

33
config/config.json Normal file
View file

@ -0,0 +1,33 @@
{
"server": {
"listenAddress": "",
"listenPort": 8080,
"protocol": "https",
"domain": "localhost:8080"
},
"date": {
"minuteGranularity": 5
},
"request": {
"secretLength": 15
},
"mailer": {
"type": "Stdout",
"fromAddress": "sprechstunden@localhost",
"fromName": "Mathebau Sprechstunden <sprechstunden@localhost>",
"smtpHost": "localhost",
"smtpPort": 25,
"smtpUseAuth": false,
"smtpIdentity": "",
"smtpPassword": ""
},
"SQL": {
"type": "SQLite",
"SQLiteFile": "officeHours.db",
"mysqlUser": "officeHours",
"mysqlPassword": "",
"mysqlHost": "localhost",
"mysqlPort": 3306,
"mysqlDatabase": "officeHours"
}
}

View file

@ -77,14 +77,14 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ
errors = append(errors, "Die Stunde muss eine ganze Zahl sein.") errors = append(errors, "Die Stunde muss eine ganze Zahl sein.")
} }
if !(hour >= 8 && hour <= 17) { if !(hour >= 8 && hour <= 17) {
errors = append(errors, fmt.Sprintf("Sprechstunden müssen zwischen 08:00 Uhr und 17:%d starten.", 60-models.MinuteGranularity)) errors = append(errors, fmt.Sprintf("Sprechstunden müssen zwischen 08:00 Uhr und 17:%d starten.", 60-b.config.Date.MinuteGranularity))
} }
minute, err = strconv.Atoi(time[1]) minute, err = strconv.Atoi(time[1])
if err != nil { if err != nil {
errors = append(errors, "Die Minute muss eine ganze Zahl sein.") errors = append(errors, "Die Minute muss eine ganze Zahl sein.")
} }
if !(minute >= 0 && minute <= 60-models.MinuteGranularity && minute%models.MinuteGranularity == 0) { if !(minute >= 0 && minute <= 60-b.config.Date.MinuteGranularity && minute%b.config.Date.MinuteGranularity == 0) {
errors = append(errors, fmt.Sprintf("Sprechstunden dürfen nur alle %d Minuten starten.", models.MinuteGranularity)) errors = append(errors, fmt.Sprintf("Sprechstunden dürfen nur alle %d Minuten starten.", b.config.Date.MinuteGranularity))
} }
} }
date := models.Date{week, day, hour, minute} date := models.Date{week, day, hour, minute}
@ -92,8 +92,8 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ
if err != nil { if err != nil {
errors = append(errors, "Die Dauer muss eine ganze Zahl sein.") errors = append(errors, "Die Dauer muss eine ganze Zahl sein.")
} }
if !(duration >= models.MinuteGranularity && duration <= 120 && duration%models.MinuteGranularity == 0) { if !(duration >= b.config.Date.MinuteGranularity && duration <= 120 && duration%b.config.Date.MinuteGranularity == 0) {
errors = append(errors, fmt.Sprintf("Sprechstunden müssen zwischen %d und 120 Minuten lang sein.", models.MinuteGranularity)) errors = append(errors, fmt.Sprintf("Sprechstunden müssen zwischen %d und 120 Minuten lang sein.", b.config.Date.MinuteGranularity))
} }
roomname := req.FormValue("raumname") roomname := req.FormValue("raumname")
@ -120,7 +120,7 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ
var data maskData = maskData{ var data maskData = maskData{
courses, courses,
rooms, rooms,
models.MinuteGranularity, b.config.Date.MinuteGranularity,
courseid, courseid,
roomid, roomid,
date, date,

View file

@ -1,6 +1,9 @@
package controllers package controllers
import "sprechstundentool/models" import (
"sprechstundentool/config"
"sprechstundentool/models"
)
// BaseHandler will hold everything that controller needs // BaseHandler will hold everything that controller needs
type BaseHandler struct { type BaseHandler struct {
@ -9,9 +12,15 @@ type BaseHandler struct {
courseRepo models.CourseRepository courseRepo models.CourseRepository
tutorRepo models.TutorRepository tutorRepo models.TutorRepository
requestRepo models.RequestRepository requestRepo models.RequestRepository
config config.Config
} }
// NewBaseHandler returns a new BaseHandler // NewBaseHandler returns a new BaseHandler
func NewBaseHandler(roomRepo models.RoomRepository, officeHourRepo models.OfficeHourRepository, courseRepo models.CourseRepository, tutorRepo models.TutorRepository, requestRepo models.RequestRepository) *BaseHandler { func NewBaseHandler(roomRepo models.RoomRepository,
return &BaseHandler{roomRepo, officeHourRepo, courseRepo, tutorRepo, requestRepo} officeHourRepo models.OfficeHourRepository,
courseRepo models.CourseRepository,
tutorRepo models.TutorRepository,
requestRepo models.RequestRepository,
config config.Config) *BaseHandler {
return &BaseHandler{roomRepo, officeHourRepo, courseRepo, tutorRepo, requestRepo, config}
} }

View file

@ -21,7 +21,7 @@ func (b *BaseHandler) DeleteOfficeHourHandler(w http.ResponseWriter, req *http.R
Templates.ExecuteTemplate(w, "deleteSuccess.html", struct{}{}) Templates.ExecuteTemplate(w, "deleteSuccess.html", struct{}{})
} else { } else {
officeHours, _ := b.officeHourRepo.GetAll(true) officeHours, _ := b.officeHourRepo.GetAll(true)
timetable, slots := GetTimetable(officeHours) timetable, slots := b.GetTimetable(officeHours)
b.writeTimetablePage(w, req, printTimetable(timetable, slots, true)) b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, true))
} }
} }

View file

@ -20,8 +20,8 @@ func (b *BaseHandler) GetByRoomHandler(w http.ResponseWriter, req *http.Request)
return return
} }
officeHours, _ := b.officeHourRepo.FindByRoom(room, true) officeHours, _ := b.officeHourRepo.FindByRoom(room, true)
timetable, slots := GetTimetable(officeHours) timetable, slots := b.GetTimetable(officeHours)
b.writeTimetablePage(w, req, printTimetable(timetable, slots, false)) b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, false))
} }
func (b *BaseHandler) GetByCourseHandler(w http.ResponseWriter, req *http.Request) { func (b *BaseHandler) GetByCourseHandler(w http.ResponseWriter, req *http.Request) {
@ -35,8 +35,8 @@ func (b *BaseHandler) GetByCourseHandler(w http.ResponseWriter, req *http.Reques
return return
} }
officeHours, _ := b.officeHourRepo.FindByCourse(course, true) officeHours, _ := b.officeHourRepo.FindByCourse(course, true)
timetable, slots := GetTimetable(officeHours) timetable, slots := b.GetTimetable(officeHours)
b.writeTimetablePage(w, req, printTimetable(timetable, slots, false)) b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, false))
} }
func (b *BaseHandler) writeTimetablePage(w http.ResponseWriter, req *http.Request, timetable template.HTML) { func (b *BaseHandler) writeTimetablePage(w http.ResponseWriter, req *http.Request, timetable template.HTML) {

View file

@ -8,11 +8,11 @@ import (
"sprechstundentool/models" "sprechstundentool/models"
) )
func GetTimetable(officeHours []models.OfficeHour) (timetable map[models.Date]map[int]models.OfficeHour, slots []int) { func (b *BaseHandler) GetTimetable(officeHours []models.OfficeHour) (timetable map[models.Date]map[int]models.OfficeHour, slots []int) {
var fullTimetable = make(map[models.Date]map[int]models.OfficeHour) var fullTimetable = make(map[models.Date]map[int]models.OfficeHour)
for _, officeHour := range officeHours { for _, officeHour := range officeHours {
var slot int = 0 var slot int = 0
for minute := 0; minute < officeHour.Duration; minute += models.MinuteGranularity { // find slot id for minute := 0; minute < officeHour.Duration; minute += b.config.Date.MinuteGranularity { // find slot id
_, exists := fullTimetable[models.GetEndDate(officeHour.Date, minute, true)] _, exists := fullTimetable[models.GetEndDate(officeHour.Date, minute, true)]
if exists { if exists {
_, exists := fullTimetable[models.GetEndDate(officeHour.Date, minute, true)][slot] _, exists := fullTimetable[models.GetEndDate(officeHour.Date, minute, true)][slot]
@ -25,7 +25,7 @@ func GetTimetable(officeHours []models.OfficeHour) (timetable map[models.Date]ma
fullTimetable[models.GetEndDate(officeHour.Date, minute, true)] = make(map[int]models.OfficeHour) fullTimetable[models.GetEndDate(officeHour.Date, minute, true)] = make(map[int]models.OfficeHour)
} }
} }
for minute := 0; minute < officeHour.Duration; minute += models.MinuteGranularity { // write officeHour id to timetable for minute := 0; minute < officeHour.Duration; minute += b.config.Date.MinuteGranularity { // write officeHour id to timetable
fullTimetable[models.GetEndDate(officeHour.Date, minute, true)][slot] = officeHour fullTimetable[models.GetEndDate(officeHour.Date, minute, true)][slot] = officeHour
} }
} }
@ -47,10 +47,10 @@ func GetTimetable(officeHours []models.OfficeHour) (timetable map[models.Date]ma
return fullTimetable, slots return fullTimetable, slots
} }
func printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots []int, deleteIcons bool) template.HTML { func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots []int, deleteIcons bool) template.HTML {
var tableBody string var tableBody string
for hour := 8; hour < 19; hour += 1 { for hour := 8; hour < 19; hour += 1 {
for minute := 0; minute < 60; minute += models.MinuteGranularity { for minute := 0; minute < 60; minute += b.config.Date.MinuteGranularity {
tableBody += "<tr>" tableBody += "<tr>"
if minute == 0 { if minute == 0 {
tableBody += fmt.Sprintf("<td>%d Uhr</td>\n", hour) tableBody += fmt.Sprintf("<td>%d Uhr</td>\n", hour)
@ -65,10 +65,10 @@ func printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots [
var continued bool = false // is this slot occupied by the same office hour the previous minute? var continued bool = false // is this slot occupied by the same office hour the previous minute?
var predecessorExists bool var predecessorExists bool
var predecessor models.OfficeHour var predecessor models.OfficeHour
if hour > 0 && minute < models.MinuteGranularity { if hour > 0 && minute < b.config.Date.MinuteGranularity {
predecessor, predecessorExists = timetable[models.Date{0, day, hour - 1, 60 - models.MinuteGranularity}][slot] predecessor, predecessorExists = timetable[models.Date{0, day, hour - 1, 60 - b.config.Date.MinuteGranularity}][slot]
} else { } else {
predecessor, predecessorExists = timetable[models.Date{0, day, hour, minute - models.MinuteGranularity}][slot] predecessor, predecessorExists = timetable[models.Date{0, day, hour, minute - b.config.Date.MinuteGranularity}][slot]
} }
if predecessorExists { if predecessorExists {
continued = (predecessor == current) continued = (predecessor == current)
@ -83,7 +83,7 @@ func printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots [
OfficeHour models.OfficeHour OfficeHour models.OfficeHour
MinuteGranularity int MinuteGranularity int
DeleteIcons bool DeleteIcons bool
}{current, models.MinuteGranularity, deleteIcons} }{current, b.config.Date.MinuteGranularity, deleteIcons}
Templates.ExecuteTemplate(&celldata, "td.html", data) Templates.ExecuteTemplate(&celldata, "td.html", data)
tableBody += celldata.String() tableBody += celldata.String()
} }

40
main.go
View file

@ -1,42 +1,47 @@
package main package main
import ( import (
"database/sql" "flag"
"fmt"
"log" "log"
"log/syslog" "log/syslog"
"net/http" "net/http"
"os" "os"
"sprechstundentool/config"
"sprechstundentool/controllers" "sprechstundentool/controllers"
"sprechstundentool/repositories" "sprechstundentool/repositories"
"sprechstundentool/sqldb" "sprechstundentool/sqldb"
"strings"
) )
func main() { func main() {
logwriter, e := syslog.New(syslog.LOG_ERR, "sprechstunden") logwriter, e := syslog.New(syslog.LOG_ERR, "office hours")
if e == nil { if e == nil {
log.SetOutput(logwriter) log.SetOutput(logwriter)
} }
configFile := flag.String(
var db *sql.DB "config",
switch os.Getenv("ohtDbType") { "config/config.json",
case "mysql": "File path to the configuration file")
db = sqldb.ConnectMysql(os.Getenv("ohtDbMysqlConnection")) flag.Parse()
default: if *configFile == "" {
if os.Getenv("ohtDbFile") != "" && !strings.Contains(os.Getenv("ohtDbFile"), "/") { flag.Usage()
db = sqldb.ConnectSQLite(os.Getenv("ohtDbFile")) os.Exit(1)
} else {
db = sqldb.ConnectSQLite("sprechstunden.db")
}
} }
var conf config.Config
err := config.ReadConfigFile(*configFile, &conf)
if err != nil {
log.Fatalf("%s: %s", "Reading JSON config file into config structure", err)
}
db := sqldb.Connect(conf)
// Create repos // Create repos
roomRepo := repositories.NewRoomRepo(db) roomRepo := repositories.NewRoomRepo(db)
courseRepo := repositories.NewCourseRepo(db) courseRepo := repositories.NewCourseRepo(db)
tutorRepo := repositories.NewTutorRepo(db) tutorRepo := repositories.NewTutorRepo(db)
officeHourRepo := repositories.NewOfficeHourRepo(db, roomRepo, tutorRepo, courseRepo) officeHourRepo := repositories.NewOfficeHourRepo(db, roomRepo, tutorRepo, courseRepo)
requestRepo := repositories.NewRequestRepo(db, officeHourRepo) requestRepo := repositories.NewRequestRepo(db, officeHourRepo, conf)
h := controllers.NewBaseHandler(roomRepo, officeHourRepo, courseRepo, tutorRepo, requestRepo) h := controllers.NewBaseHandler(roomRepo, officeHourRepo, courseRepo, tutorRepo, requestRepo, conf)
http.HandleFunc("/getByRoom", h.GetByRoomHandler) http.HandleFunc("/getByRoom", h.GetByRoomHandler)
http.HandleFunc("/getByCourse", h.GetByCourseHandler) http.HandleFunc("/getByCourse", h.GetByCourseHandler)
@ -45,5 +50,6 @@ func main() {
http.HandleFunc("/deleteOfficeHour", h.DeleteOfficeHourHandler) http.HandleFunc("/deleteOfficeHour", h.DeleteOfficeHourHandler)
http.HandleFunc("/", h.RootHandler) http.HandleFunc("/", h.RootHandler)
http.ListenAndServe(":8080", nil) err = http.ListenAndServe(fmt.Sprintf("%s:%d", conf.Server.ListenAddress, conf.Server.ListenPort), nil)
fmt.Println(err.Error())
} }

View file

@ -8,8 +8,6 @@ type Date struct {
Minute int Minute int
} }
const MinuteGranularity int = 5
func DayName(day int) string { func DayName(day int) string {
switch day { switch day {
case 0: case 0:

View file

@ -11,8 +11,6 @@ type Request struct {
const RequestActivate int = 1 const RequestActivate int = 1
const RequestDelete int = 2 const RequestDelete int = 2
const SecretLength int = 15
type RequestRepository interface { type RequestRepository interface {
Add(officeHour OfficeHour, action int) (int, error) Add(officeHour OfficeHour, action int) (int, error)
FindBySecret(secret string) (Request, error) FindBySecret(secret string) (Request, error)

View file

@ -114,7 +114,10 @@ func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) (id int, err error) {
func (r *OfficeHourRepo) Delete(officeHour models.OfficeHour) error { func (r *OfficeHourRepo) Delete(officeHour models.OfficeHour) error {
_, err := r.db.Exec("DELETE FROM officeHour WHERE id=?", officeHour.Id) _, err := r.db.Exec("DELETE FROM officeHour WHERE id=?", officeHour.Id)
return fmt.Errorf("Error deleting officeHour from database: %s", err.Error()) if err != nil {
return fmt.Errorf("Error deleting officeHour from database: %s", err.Error())
}
return nil
} }
func (r *OfficeHourRepo) getFromRow(row *sql.Row) (models.OfficeHour, error) { func (r *OfficeHourRepo) getFromRow(row *sql.Row) (models.OfficeHour, error) {

View file

@ -5,8 +5,11 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"database/sql" "database/sql"
"fmt"
"log"
"math/big" "math/big"
"net/smtp" "net/smtp"
"sprechstundentool/config"
"sprechstundentool/controllers" "sprechstundentool/controllers"
"sprechstundentool/models" "sprechstundentool/models"
) )
@ -14,12 +17,14 @@ import (
type RequestRepo struct { type RequestRepo struct {
db *sql.DB db *sql.DB
officeHourRepo models.OfficeHourRepository officeHourRepo models.OfficeHourRepository
config config.Config
} }
func NewRequestRepo(db *sql.DB, officeHourRepo models.OfficeHourRepository) *RequestRepo { func NewRequestRepo(db *sql.DB, officeHourRepo models.OfficeHourRepository, config config.Config) *RequestRepo {
return &RequestRepo{ return &RequestRepo{
db: db, db: db,
officeHourRepo: officeHourRepo, officeHourRepo: officeHourRepo,
config: config,
} }
} }
@ -68,8 +73,8 @@ func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error)
* but don't insert new request into database. * but don't insert new request into database.
*/ */
for _, request := range existents { for _, request := range existents {
if request.Action == action { // already covered by selection && request.OfficeHour == officeHour { if request.Action == action { // already covered by selection: && request.OfficeHour == officeHour {
return request.Id, sendConfirmationMail(request) return request.Id, r.sendConfirmationMail(request)
} }
} }
secret, err := r.newSecret() secret, err := r.newSecret()
@ -85,7 +90,7 @@ func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error)
if err != nil { if err != nil {
return request.Id, err return request.Id, err
} }
return request.Id, sendConfirmationMail(request) return request.Id, r.sendConfirmationMail(request)
} }
func (r *RequestRepo) Execute(request models.Request) error { func (r *RequestRepo) Execute(request models.Request) error {
@ -104,29 +109,42 @@ func (r *RequestRepo) Execute(request models.Request) error {
} }
func (r *RequestRepo) newSecret() (string, error) { func (r *RequestRepo) newSecret() (string, error) {
secret := randomString(models.SecretLength) var err error
var secret string
_, err := r.FindBySecret(secret)
if err != nil && err != sql.ErrNoRows {
return "", err
}
// find unused secret // find unused secret
for err != sql.ErrNoRows { for err != sql.ErrNoRows {
secret = randomString(models.SecretLength) secret = randomString(r.config.Request.SecretLength)
_, err = r.FindBySecret(secret) _, err = r.FindBySecret(secret)
if err != nil && err != sql.ErrNoRows {
return "", err
}
} }
return secret, nil return secret, nil
} }
func sendConfirmationMail(request models.Request) error { func (r *RequestRepo) sendConfirmationMail(request models.Request) error {
to := []string{request.OfficeHour.Tutor.Email}
var message bytes.Buffer var message bytes.Buffer
err := controllers.Templates.ExecuteTemplate(&message, "confirmationMail", request) var data = struct {
Config config.Config
Request models.Request
}{r.config, request}
err := controllers.Templates.ExecuteTemplate(&message, "confirmationMail", data)
if err != nil { if err != nil {
log.Printf("Error parsing confirmation Mail: %s", err.Error())
return err return err
} }
err = smtp.SendMail("192.168.0.24:25", nil, "Mathebau Sprechstunden <sprechstunden@mathebau.de>", to, message.Bytes()) switch r.config.Mailer.Type {
return err case "Stdout":
fmt.Println(message.String())
case "Smtp":
to := []string{request.OfficeHour.Tutor.Email}
var auth smtp.Auth
if r.config.Mailer.SmtpUseAuth {
auth = smtp.PlainAuth(r.config.Mailer.SmtpIdentity, r.config.Mailer.FromAddress, r.config.Mailer.SmtpPassword, r.config.Mailer.SmtpHost)
}
return smtp.SendMail(fmt.Sprintf("%s:%d", r.config.Mailer.SmtpHost, r.config.Mailer.SmtpPort), auth, string(r.config.Mailer.FromName), to, message.Bytes())
}
return nil
} }
func randomString(n int) string { func randomString(n int) string {

View file

@ -2,13 +2,31 @@ package sqldb
import ( import (
"database/sql" "database/sql"
"fmt"
"log" "log"
"sprechstundentool/config"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
func ConnectSQLite(file string) *sql.DB { func Connect(config config.Config) *sql.DB {
switch config.SQL.Type {
case "SQLite":
return connectSQLite(config.SQL.SQLiteFile)
case "Mysql":
return connectMysql(config.SQL.MysqlUser,
config.SQL.MysqlPassword,
config.SQL.MysqlHost,
config.SQL.MysqlPort,
config.SQL.MysqlDatabase)
default:
log.Fatal("Type of database not recognised.")
}
return nil
}
func connectSQLite(file string) *sql.DB {
db, err := sql.Open("sqlite3", file) db, err := sql.Open("sqlite3", file)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -17,16 +35,16 @@ func ConnectSQLite(file string) *sql.DB {
return db return db
} }
func ConnectMysql(connection string) *sql.DB { func connectMysql(user string, password string, address string, port int, database string) *sql.DB {
db, err := sql.Open("mysql", connection) db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", user, password, address, port, database))
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("Error connecting to database: %s", err)
} }
err = db.Ping() err = db.Ping()
// handle error // handle error
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("Error pinging database: %s", err)
} }
return db return db
} }

View file

@ -1,19 +1,19 @@
From: Mathebau Sprechstunden <sprechstunden@mathebau.de> From: {{.Config.Mailer.FromName}}
To: {{.OfficeHour.Tutor.Email}} To: {{.Request.OfficeHour.Tutor.Email}}
Subject: Sprechstunde {{if eq .Action 1}}anlegen{{end}}{{if eq .Action 2}}löschen{{end}} Subject: Sprechstunde {{if eq .Request.Action 1}}anlegen{{end}}{{if eq .Request.Action 2}}löschen{{end}}
Hallo {{.OfficeHour.Tutor.Name}}, Hallo {{.Request.OfficeHour.Tutor.Name}},
mit deiner Emailadresse soll eine Sprechstunde mit folgenden Daten {{if eq .Action 1}}angelegt werden{{end}}{{if eq .Action 2}}gelöscht werden{{end}}: mit deiner Emailadresse soll eine Sprechstunde mit folgenden Daten {{if eq .Request.Action 1}}angelegt werden{{end}}{{if eq .Request.Action 2}}gelöscht werden{{end}}:
{{.OfficeHour.Course.Name}} {{.Request.OfficeHour.Course.Name}}
{{DayName .OfficeHour.Date.Day}} {{DayName .Request.OfficeHour.Date.Day}}
{{printf "%02d" .OfficeHour.Date.Hour}}:{{printf "%02d" .OfficeHour.Date.Minute}} Uhr bis {{printf "%02d" .OfficeHour.EndDate.Hour}}:{{printf "%02d" .OfficeHour.EndDate.Minute}} Uhr {{printf "%02d" .Request.OfficeHour.Date.Hour}}:{{printf "%02d" .Request.OfficeHour.Date.Minute}} Uhr bis {{printf "%02d" .Request.OfficeHour.EndDate.Hour}}:{{printf "%02d" .Request.OfficeHour.EndDate.Minute}} Uhr
{{.OfficeHour.Tutor.Name}} {{.Request.OfficeHour.Tutor.Name}}
{{.OfficeHour.Room.Name}} {{.Request.OfficeHour.Room.Name}}
Falls dies richtig ist, so bestätige die Sprechstunde durch Abrufen der folgenden URL: Falls dies richtig ist, so bestätige die Sprechstunde durch Abrufen der folgenden URL:
https://sprechstunden.mathebau.de/confirmRequest?code={{.Secret}} {{.Config.Server.Protocol}}://{{.Config.Server.Domain}}/confirmRequest?code={{.Request.Secret}}
Solltest du diese Email nicht erwartet haben, so kannst du sie einfach ignorieren. Solltest du diese Email nicht erwartet haben, so kannst du sie einfach ignorieren.
Deine Fachschaft Mathematik Deine Fachschaft Mathematik