added docker compose
This commit is contained in:
@@ -5,6 +5,7 @@ backend/.vscode
|
||||
backend/.gitignore
|
||||
backend/logs
|
||||
backend/config.yaml
|
||||
backend/html
|
||||
|
||||
client/node_modules
|
||||
client/.gitignore
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -0,0 +1,2 @@
|
||||
data
|
||||
logs
|
||||
|
||||
18
Dockerfile
18
Dockerfile
@@ -10,7 +10,7 @@ RUN go mod download && go mod verify
|
||||
|
||||
# build the source-code
|
||||
COPY backend .
|
||||
RUN go build -ldflags "-s -w" -o dist/backend
|
||||
RUN go build -ldflags "-s -w" -o dist/golunteer
|
||||
|
||||
# build the frontend
|
||||
FROM node:current AS client-builder
|
||||
@@ -29,19 +29,27 @@ FROM alpine:latest
|
||||
WORKDIR /usr/bin/app
|
||||
|
||||
# copy the backend
|
||||
COPY --from=backend-builder /usr/src/backend/dist/backend backend
|
||||
COPY --from=backend-builder /usr/src/backend/dist/golunteer golunteer
|
||||
COPY backend/setup.sql backend/tables.sql ./
|
||||
|
||||
# copy the client-html
|
||||
COPY --from=client-builder /usr/src/client/out html
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
# Create a group and user
|
||||
RUN addgroup -S golunteer && adduser -S golunteer -G golunteer
|
||||
|
||||
EXPOSE 61016
|
||||
EXPOSE 80
|
||||
# create an empty config-file so that a bind doesn't create a directory
|
||||
RUN touch config.yaml && chown -R golunteer:golunteer config.yaml
|
||||
RUN mkdir data && chown -R golunteer:golunteer data
|
||||
RUN mkdir logs && chown -R golunteer:golunteer logs
|
||||
|
||||
# copy the config-file
|
||||
COPY backend/config-default.yaml .
|
||||
|
||||
# Tell docker that all future commands should run as the appuser user
|
||||
USER golunteer
|
||||
|
||||
# run the app
|
||||
CMD ["app"]
|
||||
CMD ["./golunteer", "data/config.yaml"]
|
||||
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@@ -2,3 +2,4 @@ config.yaml
|
||||
logs
|
||||
__debug_bin*
|
||||
database.db
|
||||
html
|
||||
7
backend/config-default.yaml
Normal file
7
backend/config-default.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
log_level: INFO
|
||||
database: data/database.db
|
||||
server:
|
||||
port: 80
|
||||
client_session:
|
||||
jwt_signature: JWT_SIGNATURE
|
||||
expire: 168h
|
||||
@@ -2,7 +2,9 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
@@ -51,6 +53,8 @@ func (config ConfigStruct) SignJWT(val any) (string, error) {
|
||||
}
|
||||
|
||||
func LoadConfig() ConfigStruct {
|
||||
ensureConfigExists()
|
||||
|
||||
Config := ConfigYaml{}
|
||||
|
||||
yamlFile, err := os.ReadFile(CONFIG_PATH)
|
||||
@@ -90,6 +94,32 @@ func LoadConfig() ConfigStruct {
|
||||
}
|
||||
}
|
||||
|
||||
func ensureConfigExists() {
|
||||
// if the config path doesn't exist, copy the example config there
|
||||
if _, err := os.Stat(CONFIG_PATH); errors.Is(err, os.ErrNotExist) {
|
||||
source, err := os.Open("config-default.yaml")
|
||||
|
||||
if err == nil {
|
||||
defer source.Close()
|
||||
|
||||
destination, err := os.Create(CONFIG_PATH)
|
||||
|
||||
if err == nil {
|
||||
defer destination.Close()
|
||||
|
||||
io.Copy(destination, source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// check for the config passed as an argument
|
||||
if len(os.Args) == 2 {
|
||||
CONFIG_PATH = os.Args[1]
|
||||
} else {
|
||||
CONFIG_PATH = "config.yaml"
|
||||
}
|
||||
|
||||
Config = LoadConfig()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var CONFIG_PATH = "config.yaml"
|
||||
var CONFIG_PATH string
|
||||
|
||||
type ConfigYaml struct {
|
||||
LogLevel string `yaml:"log_level"`
|
||||
|
||||
@@ -37,11 +37,7 @@ func init() {
|
||||
Out: os.Stdout,
|
||||
TimeFormat: time.DateTime,
|
||||
FormatLevel: func(i interface{}) string {
|
||||
if i == nil {
|
||||
return "| LOG |"
|
||||
} else {
|
||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
}
|
||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
},
|
||||
FormatFieldName: func(i interface{}) string {
|
||||
return fmt.Sprintf("%s", i)
|
||||
|
||||
@@ -30,13 +30,13 @@ func (a *Handler) postAvailability() {
|
||||
if err := a.C.BodyParser(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
logger.Info().Msgf("invalid body: %v", err)
|
||||
|
||||
} else if err := availabilities.Add(body); err != nil {
|
||||
a.Status = fiber.StatusInternalServerError
|
||||
@@ -60,13 +60,13 @@ func (a *Handler) patchAvailabilitiy() {
|
||||
if err := a.C.BodyParser(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
logger.Info().Msgf("invalid body: %v", err)
|
||||
} else if err := availabilities.Update(body); err != nil {
|
||||
a.Status = fiber.StatusInternalServerError
|
||||
|
||||
@@ -84,7 +84,7 @@ func (a *Handler) deleteAvailability() {
|
||||
|
||||
// parse the query
|
||||
} else if taskID := a.C.QueryInt("availabilityID", -1); taskID == -1 {
|
||||
logger.Log().Msg("availability-deletion failed: invalid query: doesn't include \"availabilityID\"")
|
||||
logger.Info().Msg("availability-deletion failed: invalid query: doesn't include \"availabilityID\"")
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ func (a *Handler) postEvent() {
|
||||
if err := a.C.BodyParser(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
// validate the parsed body
|
||||
} else if err := validate.Struct(body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
logger.Info().Msgf("invalid body: %v", err)
|
||||
|
||||
// create the event
|
||||
} else if err := events.Create(body); err != nil {
|
||||
@@ -52,13 +52,13 @@ func (a *Handler) patchEvent() {
|
||||
if err := a.C.BodyParser(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("ivnalid body: %v", err)
|
||||
logger.Info().Msgf("ivnalid body: %v", err)
|
||||
|
||||
// update the event
|
||||
} else if err := events.Update(body); err != nil {
|
||||
@@ -99,7 +99,7 @@ func (a *Handler) getEventUserAssignmentAvailability() {
|
||||
if events, err := a.UserName.WithUserAvailability(); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("getting events with tasks and user-availability failed: %v", err)
|
||||
logger.Info().Msgf("getting events with tasks and user-availability failed: %v", err)
|
||||
} else {
|
||||
a.Data = events
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (a *Handler) getEventsUserAssigned() {
|
||||
if events, err := a.UserName.GetAssignedEvents(); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("retrieval of user-assigned-events failed: %v", err)
|
||||
logger.Info().Msgf("retrieval of user-assigned-events failed: %v", err)
|
||||
} else {
|
||||
a.Data = events
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func (a *Handler) putEventUserAvailability() {
|
||||
if eventID := a.C.QueryInt("eventID", -1); eventID == -1 {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msg("setting user-event-availability failed: query is missing \"eventID\"")
|
||||
logger.Info().Msg("setting user-event-availability failed: query is missing \"eventID\"")
|
||||
} else {
|
||||
// parse the body
|
||||
body := a.C.Body()
|
||||
@@ -149,7 +149,7 @@ func (a *Handler) putEventUserAvailability() {
|
||||
if availabilityID, err := strconv.Atoi(string(body)); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("setting user-event-availability failed: can't get parse: %v", err)
|
||||
logger.Info().Msgf("setting user-event-availability failed: can't get parse: %v", err)
|
||||
} else {
|
||||
// if there was already a task assigned for this user-event-combi, remove it
|
||||
var taskIDs []int
|
||||
@@ -200,23 +200,23 @@ func (a *Handler) putEventAssignment() {
|
||||
} else if taskID := tasks.TaskID(a.C.QueryInt("taskID", -1)); taskID == -1 {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msg("setting event-assignment failed: query is missing \"taskID\"")
|
||||
logger.Info().Msg("setting event-assignment failed: query is missing \"taskID\"")
|
||||
|
||||
// parse the body
|
||||
} else if userName := users.UserName(a.C.Body()); userName == "" {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msg("setting event-assignment failed: body is missing")
|
||||
logger.Info().Msg("setting event-assignment failed: body is missing")
|
||||
|
||||
// check wether the user has actually entered an availability for the event
|
||||
} else if availabilityID, err := userName.GetUserAvailability(eventID); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("setting event-assignment failed: can't check users availability: %v", err)
|
||||
logger.Info().Msgf("setting event-assignment failed: can't check users availability: %v", err)
|
||||
} else if availabilityID == nil {
|
||||
a.Status = fiber.StatusConflict
|
||||
|
||||
logger.Log().Msgf("setting event-assignment failed: user %q isn't available for event with eventID = %d", userName, eventID)
|
||||
logger.Info().Msgf("setting event-assignment failed: user %q isn't available for event with eventID = %d", userName, eventID)
|
||||
|
||||
// check wether the user can be assigned for this task
|
||||
} else if check, err := userName.CheckTask(taskID); err != nil {
|
||||
@@ -226,7 +226,7 @@ func (a *Handler) putEventAssignment() {
|
||||
} else if !check {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("setting event-assignment failed: task with taskID = %d is not possible for user", taskID)
|
||||
logger.Info().Msgf("setting event-assignment failed: task with taskID = %d is not possible for user", taskID)
|
||||
|
||||
// set the availability in the database
|
||||
} else if err := eventID.SetAssignment(taskID, userName); err != nil {
|
||||
@@ -273,7 +273,7 @@ func (a *Handler) deleteEvent() {
|
||||
a.Status = fiber.StatusForbidden
|
||||
// -1 can't be valid
|
||||
} else if eventId := a.C.QueryInt("eventID", -1); eventId == -1 {
|
||||
logger.Log().Msgf("event-delete failed: \"eventID\" is missing in query")
|
||||
logger.Info().Msgf("event-delete failed: \"eventID\" is missing in query")
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
} else if err := events.Delete(eventId); err != nil {
|
||||
@@ -282,6 +282,6 @@ func (a *Handler) deleteEvent() {
|
||||
|
||||
a.Status = fiber.StatusInternalServerError
|
||||
} else {
|
||||
logger.Log().Msgf("deleted event with eventID %d", eventId)
|
||||
logger.Info().Msgf("deleted event with eventID %d", eventId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
@@ -129,6 +132,25 @@ func init() {
|
||||
app.Post("/api/login", handleLogin)
|
||||
app.Get("/api/logout", handleLogout)
|
||||
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
path := c.Path()
|
||||
|
||||
if _, err := os.Stat(filepath.Join("html", path)); errors.Is(err, os.ErrNotExist) {
|
||||
htmlPath := path + ".html"
|
||||
|
||||
if _, err := os.Stat(filepath.Join("html", htmlPath)); err == nil {
|
||||
c.Path(htmlPath)
|
||||
} else {
|
||||
logger.Debug().Msgf("%q", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.Next()
|
||||
|
||||
return nil
|
||||
})
|
||||
app.Static("/", "html")
|
||||
|
||||
// register the registered endpoints
|
||||
for method, handlers := range endpoints {
|
||||
for address, handler := range handlers {
|
||||
@@ -146,7 +168,7 @@ func init() {
|
||||
} else if !loggedIn {
|
||||
args.Status = fiber.StatusUnauthorized
|
||||
|
||||
logger.Log().Msgf("user not authorized")
|
||||
logger.Info().Msgf("user not authorized")
|
||||
} else {
|
||||
handler(&args)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func (a *Handler) getTasks() {
|
||||
func (a *Handler) postTask() {
|
||||
// check admin
|
||||
if !a.Admin {
|
||||
logger.Log().Msgf("user is not admin")
|
||||
logger.Info().Msgf("user is not admin")
|
||||
|
||||
a.Status = fiber.StatusUnauthorized
|
||||
} else {
|
||||
@@ -26,13 +26,13 @@ func (a *Handler) postTask() {
|
||||
var task tasks.Task
|
||||
|
||||
if err := a.C.BodyParser(&task); err != nil {
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(&task); err != nil {
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
logger.Info().Msgf("invalid body: %v", err)
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
@@ -48,7 +48,7 @@ func (a *Handler) postTask() {
|
||||
func (a *Handler) patchTask() {
|
||||
// check admin
|
||||
if !a.Admin {
|
||||
logger.Log().Msgf("user is not admin")
|
||||
logger.Info().Msgf("user is not admin")
|
||||
|
||||
a.Status = fiber.StatusUnauthorized
|
||||
} else {
|
||||
@@ -56,13 +56,13 @@ func (a *Handler) patchTask() {
|
||||
var task tasks.TaskDB
|
||||
|
||||
if err := a.C.BodyParser(&task); err != nil {
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(&task); err != nil {
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
logger.Info().Msgf("invalid body: %v", err)
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
@@ -84,7 +84,7 @@ func (a *Handler) deleteTask() {
|
||||
|
||||
// parse the query
|
||||
} else if taskID := a.C.QueryInt("taskID", -1); taskID == -1 {
|
||||
logger.Log().Msg("task-deletion failed: invalid query: doesn't include \"taskID\"")
|
||||
logger.Info().Msg("task-deletion failed: invalid query: doesn't include \"taskID\"")
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ func (a *Handler) getUsers() {
|
||||
if !a.Admin {
|
||||
a.Status = fiber.StatusForbidden
|
||||
|
||||
logger.Log().Msgf("user is no admin")
|
||||
logger.Info().Msgf("user is no admin")
|
||||
} else if users, err := users.Get(); err != nil {
|
||||
a.Status = fiber.StatusInternalServerError
|
||||
|
||||
@@ -26,7 +26,7 @@ func (a *Handler) postUser() {
|
||||
if !a.Admin {
|
||||
a.Status = fiber.StatusForbidden
|
||||
|
||||
logger.Log().Msgf("user is no admin")
|
||||
logger.Info().Msgf("user is no admin")
|
||||
} else {
|
||||
// parse the body
|
||||
var body users.UserAdd
|
||||
@@ -54,7 +54,7 @@ func (a *Handler) putPassword() {
|
||||
var body users.UserChangePassword
|
||||
|
||||
if err := a.C.BodyParser(&body); err != nil {
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
@@ -98,7 +98,7 @@ func (a *Handler) patchUser() {
|
||||
if !a.Admin {
|
||||
a.Status = fiber.StatusForbidden
|
||||
|
||||
logger.Log().Msgf("user is no admin")
|
||||
logger.Info().Msgf("user is no admin")
|
||||
} else {
|
||||
// parse the body
|
||||
var body struct {
|
||||
@@ -109,7 +109,7 @@ func (a *Handler) patchUser() {
|
||||
if err := a.C.BodyParser(&body); err != nil {
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
logger.Info().Msgf("can't parse body: %v", err)
|
||||
|
||||
// prevent to demoting self from admin
|
||||
} else if !body.Admin && body.UserName == a.UserName {
|
||||
@@ -196,19 +196,19 @@ func (a *Handler) deleteUser() {
|
||||
|
||||
// get the username from the query
|
||||
} else if userName := a.C.Query("userName"); userName == "" {
|
||||
logger.Log().Msg("user-deletion failed: query is missing \"userName\"")
|
||||
logger.Info().Msg("user-deletion failed: query is missing \"userName\"")
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
// check wether the user tries to delete himself
|
||||
} else if users.UserName(userName) == a.UserName {
|
||||
logger.Log().Msg("user-deletion failed: self-deletion is illegal")
|
||||
logger.Info().Msg("user-deletion failed: self-deletion is illegal")
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
// check wether the user tries to delete the admin
|
||||
} else if userName == "admin" {
|
||||
logger.Log().Msg("user-deletion failed: admin-deletion is illegal")
|
||||
logger.Info().Msg("user-deletion failed: admin-deletion is illegal")
|
||||
|
||||
a.Status = fiber.StatusBadRequest
|
||||
|
||||
|
||||
25
compose.yaml
Normal file
25
compose.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
services:
|
||||
app:
|
||||
image: z1glr/golunteer:latest
|
||||
build: .
|
||||
ports:
|
||||
- 80:80
|
||||
volumes:
|
||||
- database:/usr/bin/app/data
|
||||
- logs:/usr/bin/app/logs
|
||||
- ./config.yaml:/usr/bin/app/config.yaml
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
database:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
device: "./data"
|
||||
o: bind
|
||||
logs:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
device: "./logs"
|
||||
o: bind
|
||||
Reference in New Issue
Block a user