Compare commits
4 commits
a755a0b3e9
...
f04396809d
Author | SHA1 | Date | |
---|---|---|---|
f04396809d | |||
7a1448d6f9 | |||
961a10915b | |||
6e97d867de |
27 changed files with 355 additions and 143 deletions
|
@ -51,12 +51,14 @@ type Config struct {
|
||||||
func ReadConfigFile(filename string, conf *Config) error {
|
func ReadConfigFile(filename string, conf *Config) error {
|
||||||
configData, err := ioutil.ReadFile(filename)
|
configData, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading config file: %s", err.Error())
|
err = fmt.Errorf("Error reading config file: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(configData, conf)
|
err = json.Unmarshal(configData, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error parsing config file as json: %s", err.Error())
|
err = fmt.Errorf("Error parsing config file as json: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validateConfig(conf)
|
return validateConfig(conf)
|
||||||
|
|
|
@ -2,10 +2,12 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"officeHours/config"
|
"officeHours/config"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
|
"officeHours/templating"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -148,12 +150,19 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ
|
||||||
id, err := b.officeHourRepo.Add(officeHour)
|
id, err := b.officeHourRepo.Add(officeHour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
b.serveTemplate(w, "addFailure", err)
|
templating.ServeTemplate(w, "addFailure", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
officeHour, _ = b.officeHourRepo.FindById(id)
|
officeHour, err = b.officeHourRepo.FindById(id)
|
||||||
b.requestRepo.Add(officeHour, models.RequestActivate)
|
if err != nil {
|
||||||
b.serveTemplate(w, "addSuccess", nil)
|
log.Printf("Error finding new office hour by id %d: %s", id, err.Error())
|
||||||
|
}
|
||||||
|
_, err = b.requestRepo.Add(officeHour, models.RequestActivate)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error adding request: %s", err.Error())
|
||||||
|
}
|
||||||
|
templating.ServeTemplate(w, "addSuccess", nil)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,5 +173,5 @@ func (b *BaseHandler) writeAddOfficeHourMask(w http.ResponseWriter, req *http.Re
|
||||||
if len(data.Errors) != 0 {
|
if len(data.Errors) != 0 {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
b.serveTemplate(w, "addMask", data)
|
templating.ServeTemplate(w, "addMask", data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"officeHours/templating"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseHandler) ConfirmRequestHandler(w http.ResponseWriter, req *http.Request) {
|
func (b *BaseHandler) ConfirmRequestHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
@ -9,15 +10,17 @@ func (b *BaseHandler) ConfirmRequestHandler(w http.ResponseWriter, req *http.Req
|
||||||
request, err := b.requestRepo.FindBySecret(secret)
|
request, err := b.requestRepo.FindBySecret(secret)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.serveTemplate(w, "requestNotFound", nil)
|
// TODO: header 404
|
||||||
|
templating.ServeTemplate(w, "requestNotFound", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.requestRepo.Execute(request)
|
err = b.requestRepo.Execute(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.serveTemplate(w, "executeFailure", err.Error())
|
// TODO: write header 500
|
||||||
|
templating.ServeTemplate(w, "executeFailure", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.serveTemplate(w, "executeSuccess", nil)
|
templating.ServeTemplate(w, "executeSuccess", nil)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,13 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
|
"officeHours/templating"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseHandler) DeleteOfficeHourHandler(w http.ResponseWriter, req *http.Request) {
|
func (b *BaseHandler) DeleteOfficeHourHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
// TODO: error handling here is by no means sufficient, furthermore
|
||||||
|
// 400 BadRequest is for technically wrong stuff (most promimently GET instead of POST)
|
||||||
if req.FormValue("id") != "" {
|
if req.FormValue("id") != "" {
|
||||||
id, err := strconv.Atoi(req.FormValue("id"))
|
id, err := strconv.Atoi(req.FormValue("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,7 +21,7 @@ func (b *BaseHandler) DeleteOfficeHourHandler(w http.ResponseWriter, req *http.R
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
_, err = b.requestRepo.Add(officeHour, models.RequestDelete)
|
_, err = b.requestRepo.Add(officeHour, models.RequestDelete)
|
||||||
b.serveTemplate(w, "deleteSuccess", nil)
|
templating.ServeTemplate(w, "deleteSuccess", nil)
|
||||||
} else {
|
} else {
|
||||||
officeHours, _ := b.officeHourRepo.GetAll(true)
|
officeHours, _ := b.officeHourRepo.GetAll(true)
|
||||||
timetable, slots := b.GetTimetable(officeHours)
|
timetable, slots := b.GetTimetable(officeHours)
|
||||||
|
|
|
@ -2,8 +2,10 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
|
"officeHours/templating"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +20,10 @@ func (b *BaseHandler) GetByRoomHandler(w http.ResponseWriter, req *http.Request)
|
||||||
b.RootHandler(w, req)
|
b.RootHandler(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
officeHours, _ := b.officeHourRepo.FindByRoom(room, true)
|
officeHours, err := b.officeHourRepo.FindByRoom(room, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting office hours for room %s: %s", room.Name, err.Error())
|
||||||
|
}
|
||||||
timetable, slots := b.GetTimetable(officeHours)
|
timetable, slots := b.GetTimetable(officeHours)
|
||||||
b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, false))
|
b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, false))
|
||||||
}
|
}
|
||||||
|
@ -34,7 +39,10 @@ func (b *BaseHandler) GetByCourseHandler(w http.ResponseWriter, req *http.Reques
|
||||||
b.RootHandler(w, req)
|
b.RootHandler(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
officeHours, _ := b.officeHourRepo.FindByCourse(course, true)
|
officeHours, err := b.officeHourRepo.FindByCourse(course, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting office hours for course %s: %s", course.Name, err.Error())
|
||||||
|
}
|
||||||
timetable, slots := b.GetTimetable(officeHours)
|
timetable, slots := b.GetTimetable(officeHours)
|
||||||
b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, false))
|
b.writeTimetablePage(w, req, b.printTimetable(timetable, slots, false))
|
||||||
}
|
}
|
||||||
|
@ -51,5 +59,5 @@ func (b *BaseHandler) writeTimetablePage(w http.ResponseWriter, req *http.Reques
|
||||||
SelectedRoom int
|
SelectedRoom int
|
||||||
SelectedCourse int
|
SelectedCourse int
|
||||||
}{courses, rooms, timetable, selectedRoom, selectedCourse}
|
}{courses, rooms, timetable, selectedRoom, selectedCourse}
|
||||||
b.serveTemplate(w, "index", data)
|
templating.ServeTemplate(w, "index", data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
package controllers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"officeHours/models"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var funcs = template.FuncMap{
|
|
||||||
"DayName": models.DayName,
|
|
||||||
"divide": func(i int, j int) int { return i / j },
|
|
||||||
}
|
|
||||||
var baseTemplate = template.Must(template.ParseFiles("templates/base.html")).New("base.html").Funcs(funcs)
|
|
||||||
|
|
||||||
// Execute a rendered full HTML page.
|
|
||||||
//
|
|
||||||
// If you just want to fill a template snippet, use the RawTemplates object.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - name is the name of the template file inside templates/, without .html suffix.
|
|
||||||
// - data is passed through to the template
|
|
||||||
func (b *BaseHandler) serveTemplate(w http.ResponseWriter, name string, data any) {
|
|
||||||
full_name := "templates/" + name + ".html"
|
|
||||||
// check that template exists
|
|
||||||
info, err := os.Stat(full_name)
|
|
||||||
if (err != nil && os.IsNotExist(err)) || info.IsDir() {
|
|
||||||
errMsg := fmt.Sprintf("Template %s nicht gefunden", full_name)
|
|
||||||
log.Println(errMsg)
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
w.Write([]byte(errMsg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl, err := baseTemplate.ParseFiles(full_name)
|
|
||||||
if err != nil {
|
|
||||||
errMsg := fmt.Sprintf("Template %s konnte nicht geparst werden : %s", full_name, err.Error())
|
|
||||||
log.Println(errMsg)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(errMsg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: cache templates in parsed state, but not executed
|
|
||||||
err = template.Must(tmpl.Clone()).Execute(w, data)
|
|
||||||
if err != nil {
|
|
||||||
errMsg := fmt.Sprintf("Template %s konnte nicht ausgeführt werden : %s", full_name, err.Error())
|
|
||||||
log.Println(errMsg)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(errMsg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// standalone templates that should not be wrapped into the base.html
|
|
||||||
var RawTemplates = template.Must(template.New("").Funcs(funcs).ParseFiles(
|
|
||||||
"templates/confirmationMail",
|
|
||||||
"templates/td.html",
|
|
||||||
"templates/officeHourTable.html"))
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"log"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
|
"officeHours/templating"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseHandler) 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) {
|
||||||
|
@ -84,7 +86,12 @@ func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.Of
|
||||||
MinuteGranularity int
|
MinuteGranularity int
|
||||||
DeleteIcons bool
|
DeleteIcons bool
|
||||||
}{OfficeHour: current, MinuteGranularity: b.config.Date.MinuteGranularity, DeleteIcons: deleteIcons}
|
}{OfficeHour: current, MinuteGranularity: b.config.Date.MinuteGranularity, DeleteIcons: deleteIcons}
|
||||||
RawTemplates.ExecuteTemplate(&celldata, "td.html", data)
|
err := templating.WriteTemplate(&celldata, "td", data)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("writing table cell failed:\n%w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
// TODO: better error wrapping up to top-level request handler
|
||||||
|
}
|
||||||
tableBody += celldata.String()
|
tableBody += celldata.String()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -116,6 +123,11 @@ func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.Of
|
||||||
slots[4],
|
slots[4],
|
||||||
template.HTML(tableBody),
|
template.HTML(tableBody),
|
||||||
}
|
}
|
||||||
RawTemplates.ExecuteTemplate(&table, "officeHourTable.html", tableData)
|
err := templating.WriteTemplate(&table, "officeHourTable", tableData)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("writing table failed:\n%w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
// TODO: better error wrapping up to top-level request handler
|
||||||
|
}
|
||||||
return template.HTML(table.String())
|
return template.HTML(table.String())
|
||||||
}
|
}
|
||||||
|
|
15
main.go
15
main.go
|
@ -11,6 +11,7 @@ import (
|
||||||
"officeHours/controllers"
|
"officeHours/controllers"
|
||||||
"officeHours/repositories"
|
"officeHours/repositories"
|
||||||
"officeHours/sqldb"
|
"officeHours/sqldb"
|
||||||
|
"officeHours/templating"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ func main() {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
log.SetOutput(logwriter)
|
log.SetOutput(logwriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
configFile := flag.String(
|
configFile := flag.String(
|
||||||
"config",
|
"config",
|
||||||
"config/config.json",
|
"config/config.json",
|
||||||
|
@ -37,8 +39,17 @@ func main() {
|
||||||
|
|
||||||
// serve static files
|
// serve static files
|
||||||
staticHandler := http.FileServer(http.Dir("./static"))
|
staticHandler := http.FileServer(http.Dir("./static"))
|
||||||
|
// parse templates
|
||||||
db := sqldb.Connect(conf)
|
if err = templating.InitTemplates(); err != nil {
|
||||||
|
log.Print(err.Error())
|
||||||
|
log.Fatal(">>>\nFatal error occurred, aborting program\n<<<\n")
|
||||||
|
}
|
||||||
|
// database connection
|
||||||
|
db, err := sqldb.Connect(conf)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err.Error())
|
||||||
|
log.Fatal(">>>\nFatal error occurred, aborting program\n<<<\n")
|
||||||
|
}
|
||||||
// Create repos
|
// Create repos
|
||||||
roomRepo := repositories.NewRoomRepo(db)
|
roomRepo := repositories.NewRoomRepo(db)
|
||||||
courseRepo := repositories.NewCourseRepo(db)
|
courseRepo := repositories.NewCourseRepo(db)
|
||||||
|
|
|
@ -3,6 +3,9 @@ package repositories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,26 +20,17 @@ func NewCourseRepo(db *sql.DB) *CourseRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CourseRepo) FindByName(name string) (models.Course, error) {
|
func (r *CourseRepo) FindByName(name string) (models.Course, error) {
|
||||||
row := r.db.QueryRow("SELECT * FROM course WHERE name=?", name)
|
return r.getFromRow(r.db.QueryRow("SELECT * FROM course WHERE name=?", name))
|
||||||
var course models.Course
|
|
||||||
if err := row.Scan(&course.Id, &course.Name); err != nil {
|
|
||||||
return models.Course{}, err
|
|
||||||
}
|
|
||||||
return course, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CourseRepo) FindById(id int) (models.Course, error) {
|
func (r *CourseRepo) FindById(id int) (models.Course, error) {
|
||||||
row := r.db.QueryRow("SELECT * FROM course WHERE id=?", id)
|
return r.getFromRow(r.db.QueryRow("SELECT * FROM course WHERE id=?", id))
|
||||||
var course models.Course
|
|
||||||
if err := row.Scan(&course.Id, &course.Name); err != nil {
|
|
||||||
return models.Course{}, err
|
|
||||||
}
|
|
||||||
return course, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CourseRepo) GetAll() ([]models.Course, error) {
|
func (r *CourseRepo) GetAll() ([]models.Course, error) {
|
||||||
rows, err := r.db.Query("SELECT * FROM course")
|
rows, err := r.db.Query("SELECT * FROM course")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error getting all courses: %s", err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
@ -49,9 +43,22 @@ func (r *CourseRepo) getFromRows(rows *sql.Rows) ([]models.Course, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var course models.Course
|
var course models.Course
|
||||||
if err := rows.Scan(&course.Id, &course.Name); err != nil {
|
if err := rows.Scan(&course.Id, &course.Name); err != nil {
|
||||||
|
log.Printf("Error scanning course row: %s", err.Error())
|
||||||
return courses, err
|
return courses, err
|
||||||
}
|
}
|
||||||
courses = append(courses, course)
|
courses = append(courses, course)
|
||||||
}
|
}
|
||||||
return courses, nil
|
return courses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *CourseRepo) getFromRow(row *sql.Row) (models.Course, error) {
|
||||||
|
var course models.Course
|
||||||
|
if err := row.Scan(&course.Id, &course.Name); err != nil {
|
||||||
|
err = fmt.Errorf("Error getting course row: %w", err)
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
log.Printf(err.Error())
|
||||||
|
}
|
||||||
|
return models.Course{}, err
|
||||||
|
}
|
||||||
|
return course, nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package repositories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"officeHours/config"
|
"officeHours/config"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
)
|
)
|
||||||
|
@ -35,7 +37,11 @@ func (r *OfficeHourRepo) GetAll(activeOnly bool) ([]models.OfficeHour, error) {
|
||||||
rows, err = r.db.Query("SELECT * FROM officeHour")
|
rows, err = r.db.Query("SELECT * FROM officeHour")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting all officeHours from database: %s", err.Error())
|
err = fmt.Errorf("Error getting all officeHours from database: %w", err)
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
return r.getFromRows(rows)
|
return r.getFromRows(rows)
|
||||||
|
@ -49,10 +55,14 @@ func (r *OfficeHourRepo) FindByCourse(course models.Course, activeOnly bool) ([]
|
||||||
} else {
|
} else {
|
||||||
rows, err = r.db.Query("SELECT * FROM officeHour WHERE course=?", course.Id)
|
rows, err = r.db.Query("SELECT * FROM officeHour WHERE course=?", course.Id)
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Error getting officeHours by course from database: %s", err.Error())
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error getting officeHours by course from database: %w", err)
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return r.getFromRows(rows)
|
return r.getFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +75,11 @@ func (r *OfficeHourRepo) FindByRoom(room models.Room, activeOnly bool) ([]models
|
||||||
rows, err = r.db.Query("SELECT * FROM officeHour WHERE room=?", room.Id)
|
rows, err = r.db.Query("SELECT * FROM officeHour WHERE room=?", room.Id)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting officeHours by room from database: %s", err.Error())
|
err = fmt.Errorf("Error getting officeHours by room from database: %w", err)
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
return r.getFromRows(rows)
|
return r.getFromRows(rows)
|
||||||
|
@ -75,11 +89,16 @@ func (r *OfficeHourRepo) FindById(id int) (models.OfficeHour, error) {
|
||||||
return r.getFromRow(r.db.QueryRow("SELECT * FROM officeHour WHERE id=?", id))
|
return r.getFromRow(r.db.QueryRow("SELECT * FROM officeHour WHERE id=?", id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) (id int, err error) {
|
func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) (int, error) {
|
||||||
// Find correct tutor or add if not existent
|
// Find correct tutor or add if not existent
|
||||||
r.tutorRepo.Add(officeHour.Tutor)
|
_, err := r.tutorRepo.Add(officeHour.Tutor)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
officeHour.Tutor, err = r.tutorRepo.FindByNameAndEmail(officeHour.Tutor.Name, officeHour.Tutor.Email)
|
officeHour.Tutor, err = r.tutorRepo.FindByNameAndEmail(officeHour.Tutor.Name, officeHour.Tutor.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Newly added tutor not found: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,14 +130,19 @@ func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) (id int, err error) {
|
||||||
officeHour.Info,
|
officeHour.Info,
|
||||||
officeHour.Active,
|
officeHour.Active,
|
||||||
officeHour.Duration)
|
officeHour.Duration)
|
||||||
id64, _ := sqlResult.LastInsertId()
|
id, lastInsertIdErr := sqlResult.LastInsertId()
|
||||||
return int(id64), err
|
if lastInsertIdErr != nil {
|
||||||
|
log.Printf("Error getting Id for new tutor: %s", lastInsertIdErr.Error())
|
||||||
|
}
|
||||||
|
return int(id), err
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error deleting officeHour from database: %s", err.Error())
|
err = fmt.Errorf("Error deleting officeHour from database: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -128,12 +152,29 @@ func (r *OfficeHourRepo) getFromRow(row *sql.Row) (models.OfficeHour, error) {
|
||||||
var week, day, hour, minute, tutorId, courseId, roomId int
|
var week, day, hour, minute, tutorId, courseId, roomId int
|
||||||
err := row.Scan(&officeHour.Id, &tutorId, &day, &hour, &minute, &roomId, &courseId, &week, &officeHour.Info, &officeHour.Active, &officeHour.Duration)
|
err := row.Scan(&officeHour.Id, &tutorId, &day, &hour, &minute, &roomId, &courseId, &week, &officeHour.Info, &officeHour.Active, &officeHour.Duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.OfficeHour{}, fmt.Errorf("Error getting single officeHours row from database: %s", err.Error())
|
err = fmt.Errorf("Error getting single officeHour row from database: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return models.OfficeHour{}, err
|
||||||
}
|
}
|
||||||
officeHour.Date = models.Date{Week: week, Day: day, Hour: hour, Minute: minute}
|
officeHour.Date = models.Date{Week: week, Day: day, Hour: hour, Minute: minute}
|
||||||
officeHour.Room, _ = r.roomRepo.FindById(roomId)
|
officeHour.Room, err = r.roomRepo.FindById(roomId)
|
||||||
officeHour.Tutor, _ = r.tutorRepo.FindById(tutorId)
|
if err != nil {
|
||||||
officeHour.Course, _ = r.courseRepo.FindById(courseId)
|
err = fmt.Errorf("Error finding room by id %d for office hour: %w", roomId, err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return officeHour, err
|
||||||
|
}
|
||||||
|
officeHour.Tutor, err = r.tutorRepo.FindById(tutorId)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error finding tutor by id %d for office hour: %w", tutorId, err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return officeHour, err
|
||||||
|
}
|
||||||
|
officeHour.Course, err = r.courseRepo.FindById(courseId)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error finding course by id %d for office hour: %w", courseId, err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return officeHour, err
|
||||||
|
}
|
||||||
return officeHour, nil
|
return officeHour, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,13 +183,29 @@ func (r *OfficeHourRepo) getFromRows(rows *sql.Rows) ([]models.OfficeHour, error
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var officeHour models.OfficeHour
|
var officeHour models.OfficeHour
|
||||||
var week, day, hour, minute, tutorId, roomId, courseId int
|
var week, day, hour, minute, tutorId, roomId, courseId int
|
||||||
|
var err error
|
||||||
if err := rows.Scan(&officeHour.Id, &tutorId, &day, &hour, &minute, &roomId, &courseId, &week, &officeHour.Info, &officeHour.Active, &officeHour.Duration); err != nil {
|
if err := rows.Scan(&officeHour.Id, &tutorId, &day, &hour, &minute, &roomId, &courseId, &week, &officeHour.Info, &officeHour.Active, &officeHour.Duration); err != nil {
|
||||||
return officeHours, fmt.Errorf("Error getting multiple officeHour rows from database: %s", err.Error())
|
return officeHours, fmt.Errorf("Error getting multiple officeHour rows from database: %w", err)
|
||||||
}
|
}
|
||||||
officeHour.Date = models.Date{Week: week, Day: day, Hour: hour, Minute: minute}
|
officeHour.Date = models.Date{Week: week, Day: day, Hour: hour, Minute: minute}
|
||||||
officeHour.Room, _ = r.roomRepo.FindById(roomId)
|
officeHour.Room, err = r.roomRepo.FindById(roomId)
|
||||||
officeHour.Tutor, _ = r.tutorRepo.FindById(tutorId)
|
if err != nil {
|
||||||
officeHour.Course, _ = r.courseRepo.FindById(courseId)
|
err = fmt.Errorf("Error finding room by id %d for office hour: %w", roomId, err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return officeHours, err
|
||||||
|
}
|
||||||
|
officeHour.Tutor, err = r.tutorRepo.FindById(tutorId)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error finding tutor by id %d for office hour: %w", tutorId, err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return officeHours, err
|
||||||
|
}
|
||||||
|
officeHour.Course, err = r.courseRepo.FindById(courseId)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error finding course by id %d for office hour: %w", courseId, err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return officeHours, err
|
||||||
|
}
|
||||||
officeHours = append(officeHours, officeHour)
|
officeHours = append(officeHours, officeHour)
|
||||||
}
|
}
|
||||||
return officeHours, nil
|
return officeHours, nil
|
||||||
|
@ -163,7 +220,9 @@ func (r *OfficeHourRepo) NumberByTimeSpanAndRoom(date models.Date, duration int,
|
||||||
rows, err = r.db.Query("SELECT * FROM officeHour WHERE room=?", room.Id)
|
rows, err = r.db.Query("SELECT * FROM officeHour WHERE room=?", room.Id)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("Error getting officeHours by timespan and room from database: %s", err.Error())
|
err = fmt.Errorf("Error getting officeHours by timespan and room from database: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
officeHours, err := r.getFromRows(rows)
|
officeHours, err := r.getFromRows(rows)
|
||||||
|
|
|
@ -5,13 +5,14 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"officeHours/config"
|
"officeHours/config"
|
||||||
"officeHours/controllers"
|
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
|
"officeHours/templating"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RequestRepo struct {
|
type RequestRepo struct {
|
||||||
|
@ -53,6 +54,8 @@ func (r *RequestRepo) FindByOfficeHour(officeHour models.OfficeHour) ([]models.R
|
||||||
var request models.Request
|
var request models.Request
|
||||||
var officeHourId int
|
var officeHourId int
|
||||||
if err := rows.Scan(&request.Id, &officeHourId, &request.Action, &request.Secret); err != nil {
|
if err := rows.Scan(&request.Id, &officeHourId, &request.Action, &request.Secret); err != nil {
|
||||||
|
err = fmt.Errorf("Error scanning request row: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return requests, err
|
return requests, err
|
||||||
}
|
}
|
||||||
request.OfficeHour, err = r.officeHourRepo.FindById(officeHourId)
|
request.OfficeHour, err = r.officeHourRepo.FindById(officeHourId)
|
||||||
|
@ -66,7 +69,7 @@ func (r *RequestRepo) FindByOfficeHour(officeHour models.OfficeHour) ([]models.R
|
||||||
|
|
||||||
func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error) {
|
func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error) {
|
||||||
existents, err := r.FindByOfficeHour(officeHour)
|
existents, err := r.FindByOfficeHour(officeHour)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
/* Resend confirmation mail if identical request exists,
|
/* Resend confirmation mail if identical request exists,
|
||||||
|
@ -100,10 +103,10 @@ func (r *RequestRepo) Execute(request models.Request) error {
|
||||||
_, err = r.db.Exec("UPDATE officeHour SET active=true WHERE id=?", request.OfficeHour.Id)
|
_, err = r.db.Exec("UPDATE officeHour SET active=true WHERE id=?", request.OfficeHour.Id)
|
||||||
r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id)
|
r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id)
|
||||||
case models.RequestDelete:
|
case models.RequestDelete:
|
||||||
r.officeHourRepo.Delete(request.OfficeHour)
|
err = r.officeHourRepo.Delete(request.OfficeHour)
|
||||||
r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id)
|
r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id)
|
||||||
default:
|
default:
|
||||||
r.db.Exec("DELETE FROM request WHERE id=?", request.Id)
|
_, err = r.db.Exec("DELETE FROM request WHERE id=?", request.Id)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -112,10 +115,10 @@ func (r *RequestRepo) newSecret() (string, error) {
|
||||||
var err error
|
var err error
|
||||||
var secret string
|
var secret string
|
||||||
// find unused secret
|
// find unused secret
|
||||||
for err != sql.ErrNoRows {
|
for !errors.Is(err, sql.ErrNoRows) {
|
||||||
secret = randomString(r.config.Request.SecretLength)
|
secret = randomString(r.config.Request.SecretLength)
|
||||||
_, err = r.FindBySecret(secret)
|
_, err = r.FindBySecret(secret)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,11 +131,13 @@ func (r *RequestRepo) sendConfirmationMail(request models.Request) error {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
Request models.Request
|
Request models.Request
|
||||||
}{r.config, request}
|
}{r.config, request}
|
||||||
err := controllers.RawTemplates.ExecuteTemplate(&message, "confirmationMail", data)
|
err := templating.WriteTemplate(&message, "confirmationMail", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error parsing confirmation Mail: %s", err.Error())
|
err = fmt.Errorf("Error parsing confirmation Mail: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r.config.Mailer.Type {
|
switch r.config.Mailer.Type {
|
||||||
case "Stdout":
|
case "Stdout":
|
||||||
fmt.Println(message.String())
|
fmt.Println(message.String())
|
||||||
|
@ -142,7 +147,12 @@ func (r *RequestRepo) sendConfirmationMail(request models.Request) error {
|
||||||
if r.config.Mailer.SmtpUseAuth {
|
if r.config.Mailer.SmtpUseAuth {
|
||||||
auth = smtp.PlainAuth(r.config.Mailer.SmtpIdentity, r.config.Mailer.FromAddress, r.config.Mailer.SmtpPassword, r.config.Mailer.SmtpHost)
|
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())
|
err = smtp.SendMail(fmt.Sprintf("%s:%d", r.config.Mailer.SmtpHost, r.config.Mailer.SmtpPort), auth, string(r.config.Mailer.FromName), to, message.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error sending mail by smtp: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -154,7 +164,10 @@ func randomString(n int) string {
|
||||||
|
|
||||||
s := make([]rune, n)
|
s := make([]rune, n)
|
||||||
for i := range s {
|
for i := range s {
|
||||||
position, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
position, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting random position in randomString(n int): %s", err.Error())
|
||||||
|
}
|
||||||
s[i] = letters[position.Int64()]
|
s[i] = letters[position.Int64()]
|
||||||
}
|
}
|
||||||
return string(s)
|
return string(s)
|
||||||
|
|
|
@ -3,6 +3,9 @@ package repositories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,6 +32,10 @@ func (r *RoomRepo) FindById(id int) (models.Room, error) {
|
||||||
row := r.db.QueryRow("SELECT * FROM room WHERE id=?", id)
|
row := r.db.QueryRow("SELECT * FROM room WHERE id=?", id)
|
||||||
var room models.Room
|
var room models.Room
|
||||||
if err := row.Scan(&room.Id, &room.Name, &room.MaxOccupy); err != nil {
|
if err := row.Scan(&room.Id, &room.Name, &room.MaxOccupy); err != nil {
|
||||||
|
err = fmt.Errorf("Error scanning row to get room: %w", err)
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
return models.Room{}, err
|
return models.Room{}, err
|
||||||
}
|
}
|
||||||
return room, nil
|
return room, nil
|
||||||
|
@ -37,6 +44,8 @@ func (r *RoomRepo) FindById(id int) (models.Room, error) {
|
||||||
func (r *RoomRepo) GetAll() ([]models.Room, error) {
|
func (r *RoomRepo) GetAll() ([]models.Room, error) {
|
||||||
rows, err := r.db.Query("SELECT * FROM room")
|
rows, err := r.db.Query("SELECT * FROM room")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error getting all rooms: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
@ -45,15 +54,10 @@ func (r *RoomRepo) GetAll() ([]models.Room, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var room models.Room
|
var room models.Room
|
||||||
if err := rows.Scan(&room.Id, &room.Name, &room.MaxOccupy); err != nil {
|
if err := rows.Scan(&room.Id, &room.Name, &room.MaxOccupy); err != nil {
|
||||||
|
err = fmt.Errorf("Error scanning row to get room: %w", err)
|
||||||
return rooms, err
|
return rooms, err
|
||||||
}
|
}
|
||||||
rooms = append(rooms, room)
|
rooms = append(rooms, room)
|
||||||
}
|
}
|
||||||
return rooms, nil
|
return rooms, nil
|
||||||
}
|
}
|
||||||
func (r *RoomRepo) Save(room models.Room) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (r *RoomRepo) Add(room models.Room) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ package repositories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
"officeHours/models"
|
"officeHours/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,6 +30,8 @@ func (r *TutorRepo) FindByEmail(email string) ([]models.Tutor, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var tutor models.Tutor
|
var tutor models.Tutor
|
||||||
if err := rows.Scan(&tutor.Id, &tutor.Name, &tutor.Email); err != nil {
|
if err := rows.Scan(&tutor.Id, &tutor.Name, &tutor.Email); err != nil {
|
||||||
|
err = fmt.Errorf("Error scanning tutor row: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return tutors, err
|
return tutors, err
|
||||||
}
|
}
|
||||||
tutors = append(tutors, tutor)
|
tutors = append(tutors, tutor)
|
||||||
|
@ -63,23 +68,24 @@ func (r *TutorRepo) GetAll() ([]models.Tutor, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var tutor models.Tutor
|
var tutor models.Tutor
|
||||||
if err := rows.Scan(&tutor.Id, &tutor.Name, &tutor.Email); err != nil {
|
if err := rows.Scan(&tutor.Id, &tutor.Name, &tutor.Email); err != nil {
|
||||||
|
err = fmt.Errorf("Error scanning tutor row: %w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
return tutors, err
|
return tutors, err
|
||||||
}
|
}
|
||||||
tutors = append(tutors, tutor)
|
tutors = append(tutors, tutor)
|
||||||
}
|
}
|
||||||
return tutors, nil
|
return tutors, nil
|
||||||
}
|
}
|
||||||
func (r *TutorRepo) Save(tutor models.Tutor) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (r *TutorRepo) Add(tutor models.Tutor) (int, error) {
|
func (r *TutorRepo) Add(tutor models.Tutor) (int, error) {
|
||||||
//Don't add identical tutors
|
//Don't add identical tutors
|
||||||
existentTutor, err := r.FindByNameAndEmail(tutor.Name, tutor.Email)
|
existentTutor, err := r.FindByNameAndEmail(tutor.Name, tutor.Email)
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
sqlResult, err := r.db.Exec("INSERT INTO `tutor` (name, email) VALUES (?,?)", tutor.Name, tutor.Email)
|
sqlResult, err := r.db.Exec("INSERT INTO `tutor` (name, email) VALUES (?,?)", tutor.Name, tutor.Email)
|
||||||
id, _ := sqlResult.LastInsertId()
|
id, lastInsertIdErr := sqlResult.LastInsertId()
|
||||||
|
if lastInsertIdErr != nil {
|
||||||
|
log.Printf("Error getting Id for new tutor: %s", lastInsertIdErr.Error())
|
||||||
|
}
|
||||||
return int(id), err
|
return int(id), err
|
||||||
}
|
}
|
||||||
return existentTutor.Id, err
|
return existentTutor.Id, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,13 @@ package sqldb
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"officeHours/config"
|
"officeHours/config"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Connect(config config.Config) *sql.DB {
|
func Connect(config config.Config) (*sql.DB, error) {
|
||||||
switch config.SQL.Type {
|
switch config.SQL.Type {
|
||||||
case "SQLite":
|
case "SQLite":
|
||||||
return connectSQLite(config.SQL.SQLiteFile)
|
return connectSQLite(config.SQL.SQLiteFile)
|
||||||
|
@ -21,30 +20,28 @@ func Connect(config config.Config) *sql.DB {
|
||||||
config.SQL.MysqlPort,
|
config.SQL.MysqlPort,
|
||||||
config.SQL.MysqlDatabase)
|
config.SQL.MysqlDatabase)
|
||||||
default:
|
default:
|
||||||
log.Fatal("Type of database not recognised.")
|
return nil, fmt.Errorf("Type of database not recognised: %s", config.SQL.Type)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectSQLite(file string) *sql.DB {
|
func connectSQLite(file string) (*sql.DB, error) {
|
||||||
db, err := sql.Open("sqlite3", file)
|
db, err := sql.Open("sqlite3", file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return db, fmt.Errorf("Error opening SQLite database: %w", err)
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return db
|
func connectMysql(user string, password string, address string, port int, database string) (*sql.DB, error) {
|
||||||
}
|
|
||||||
|
|
||||||
func connectMysql(user string, password string, address string, port int, database string) *sql.DB {
|
|
||||||
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", user, password, address, port, database))
|
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.Fatalf("Error connecting to database: %s", err)
|
return db, fmt.Errorf("Error connecting to Mysql database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Ping()
|
err = db.Ping()
|
||||||
// handle error
|
// handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error pinging database: %s", err)
|
return db, fmt.Errorf("Error pinging Mysql database: %w", err)
|
||||||
}
|
}
|
||||||
return db
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
138
templating/templates.go
Normal file
138
templating/templates.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package templating
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"officeHours/models"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parsed templates available for execution
|
||||||
|
var templates map[string]*template.Template = map[string]*template.Template{}
|
||||||
|
|
||||||
|
// Initialise and parse templates.
|
||||||
|
//
|
||||||
|
// Should only be called once.
|
||||||
|
//
|
||||||
|
// Since this is something which may error and feels like
|
||||||
|
// it should not be done automatically on import,
|
||||||
|
// put it into a function.
|
||||||
|
func InitTemplates() error {
|
||||||
|
const templateDir = "templating/templates/"
|
||||||
|
var funcs = template.FuncMap{
|
||||||
|
"DayName": models.DayName,
|
||||||
|
"divide": func(i int, j int) int { return i / j },
|
||||||
|
}
|
||||||
|
var emptyTemplate = template.New("").Funcs(funcs)
|
||||||
|
var baseTemplate, err = template.ParseFiles(templateDir + "base.html")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing base template failed:\n%w", err)
|
||||||
|
}
|
||||||
|
baseTemplate.Funcs(funcs)
|
||||||
|
|
||||||
|
type toCache struct {
|
||||||
|
filename string
|
||||||
|
standalone bool
|
||||||
|
}
|
||||||
|
var toParse = map[string]toCache{
|
||||||
|
// full html templates
|
||||||
|
"addFailure": {"addFailure.html", false},
|
||||||
|
"addMask": {"addMask.html", false},
|
||||||
|
"addSuccess": {"addSuccess.html", false},
|
||||||
|
"deleteSuccess": {"deleteSuccess.html", false},
|
||||||
|
"executeFailure": {"executeFailure.html", false},
|
||||||
|
"executeSuccess": {"executeSuccess.html", false},
|
||||||
|
"index": {"index.html", false},
|
||||||
|
"requestNotFound": {"requestNotFound.html", false},
|
||||||
|
// standalone templates
|
||||||
|
"confirmationMail": {"confirmationMail", true},
|
||||||
|
"officeHourTable": {"officeHourTable.html", true},
|
||||||
|
"td": {"td.html", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse templates and add to global mapping
|
||||||
|
for key, tmpl := range toParse {
|
||||||
|
if _, exists := templates[key]; exists {
|
||||||
|
return fmt.Errorf("template '%s' already parsed", key)
|
||||||
|
}
|
||||||
|
fullName := templateDir + tmpl.filename
|
||||||
|
// check that template file
|
||||||
|
info, err := os.Stat(fullName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("adding template %s failed:\n%w", tmpl.filename, err)
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return fmt.Errorf("adding template %s failed: is a directory", tmpl.filename)
|
||||||
|
}
|
||||||
|
// parse
|
||||||
|
var parsed *template.Template
|
||||||
|
if tmpl.standalone {
|
||||||
|
parsed, err = emptyTemplate.Clone()
|
||||||
|
parsed = parsed.New(tmpl.filename)
|
||||||
|
} else {
|
||||||
|
parsed, err = baseTemplate.Clone()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloning base template failed:\n%w", err)
|
||||||
|
}
|
||||||
|
parsed, err = parsed.ParseFiles(fullName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing template %s failed:\n%w", tmpl.filename, err)
|
||||||
|
}
|
||||||
|
templates[key] = parsed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute a template and write it to the given writer.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - name: name of the template to execute
|
||||||
|
// - data: passed through to the template
|
||||||
|
func WriteTemplate(w io.Writer, name string, data any) error {
|
||||||
|
tmpl, exists := templates[name]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("template %s not available", name)
|
||||||
|
}
|
||||||
|
tmpl, err := tmpl.Clone()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloning template failed:\n%w", err)
|
||||||
|
}
|
||||||
|
err = tmpl.Execute(w, data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("template execution failed:\n%w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute a template and write it to a http writer.
|
||||||
|
//
|
||||||
|
// Similar to WriteTemplate, but in error case this adds a corresponding
|
||||||
|
// status code and writes an error message to the writer.
|
||||||
|
//
|
||||||
|
// Typically, this is the final action in handling an http request.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - name: name of the template to execute
|
||||||
|
// - data: passed through to the template
|
||||||
|
func ServeTemplate(w http.ResponseWriter, name string, data any) {
|
||||||
|
// TODO: make this return an error, handle on top of every request handler
|
||||||
|
err := WriteTemplate(w, name, data)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("writing template failed:\n%w", err)
|
||||||
|
log.Println(err.Error())
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
io.WriteString(w, `Internal Server Error.
|
||||||
|
|
||||||
|
Du kannst uns helfen, indem du folgende Fehlermeldung per Mail
|
||||||
|
an sprechstunden@mathebau.de sendest:
|
||||||
|
|
||||||
|
Fehler um\n
|
||||||
|
`+time.Now().String()+"\n"+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue