diff --git a/.github/workflows/sqlite3-migrations.yml b/.github/workflows/sqlite3-migrations.yml deleted file mode 100644 index 0e53b98..0000000 --- a/.github/workflows/sqlite3-migrations.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: SQLite3 Migrations - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./backend - - steps: - - uses: actions/checkout@v3 - - name: Install SQLite3 - run: sudo apt-get install sqlite3 - - name: Install Make - run: sudo apt-get install make - - name: Run Migrations - run: make migrate diff --git a/.gitignore b/.gitignore index 2d89407..9ad11ec 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ bin db.sqlite3 -*.png # Test binary, built with `go test -c` *.test diff --git a/Makefile b/Makefile deleted file mode 100644 index 668ccf1..0000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -# Builds a release container -build-container-release: - podman build -t ttime-server -f container/Containerfile . - -# Builds a release container and runs it -start-release: build-container-release remove-podman-containers - podman run -d -e DATABASE_URL=sqlite:release.db -p 8080:8080 --name ttime ttime-server - @echo "Started production ttime-server on http://localhost:8080" - -# Removes and stops any containers related to the project -remove-podman-containers: - podman container rm -fi ttime - -# Tests every part of the project -testall: - cd frontend && npm test - cd frontend && npm run lint - cd backend && make test - cd backend && make lint - -# Cleans up everything related to the project -clean: remove-podman-containers - podman image rm -fi ttime-server - rm -rf frontend/dist - rm -rf frontend/node_modules - rm -f ttime-server.tar.gz - cd backend && make clean - @echo "Cleaned up!" - -# Cleans up everything related to podman, not just the project. Make sure you understand what this means. -podman-clean: - podman system reset --force - -# Installs the linter, which is not included in the ubuntu repo -install-linter: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.56.2 - -# This installs just, a make alternative, which is slightly more ergonomic to use -install-just: - @echo "Installing just" - @curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin \ No newline at end of file diff --git a/README.md b/README.md index d75861a..fc55126 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ My recommendation would be to make WSL your primary development environment if y You should consult the [WSL documentation](https://docs.microsoft.com/en-us/windows/wsl/install), but for any recent version of windows, installation essentially boils down to running the following command in **PowerShell as an administrator**: ```powershell -wsl --install -d Ubuntu-22.04 # To get a somewhat recent version of Go +wsl --install ``` If you get any errors related to virtualization, you will need to enable virtualization in the BIOS. This is a common issue, and you can find a guide for your specific motherboard online. This is a one-time operation and will not affect your windows installation. This setting is usually called "VT-x" or "AMD-V" and is usually found in the CPU settings. If you can't find it, shoot me a message and I'll find it for you. diff --git a/backend/Makefile b/backend/Makefile index d005846..dcc79b4 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -13,7 +13,7 @@ MIGRATIONS_DIR = internal/database/migrations # Build target build: - $(GOBUILD) -o bin/server main.go + $(GOBUILD) -o bin/server cmd/*.go # Run target run: build @@ -43,7 +43,6 @@ update: # Migration target migrate: - @echo "If this ever fails, run make clean and try again" @echo "Migrating database $(DB_FILE) using SQL scripts in $(MIGRATIONS_DIR)" @for file in $(wildcard $(MIGRATIONS_DIR)/*.sql); do \ echo "Applying migration: $$file"; \ @@ -71,31 +70,6 @@ lint: # Default target default: build -# Generate swagger docs -.PHONY: docs -docs: - swag init -outputTypes go - -.PHONY: docfmt -docfmt: - swag fmt - -# Generate ERD -# Requires eralchemy2 -.PHONY: erd -erd: - eralchemy2 -i sqlite:///db.sqlite3 -o erd.png - -install-swag: - @echo "Installing swag" - @go get -u github.com/swaggo/swag/cmd/swag - -# Convenience target to install golangci-lint -install-lint: - @echo "Installing golangci-lint" - @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.42.1 - -# Convenience target to install just (requires sudo privileges) install-just: @echo "Installing just" @curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin diff --git a/backend/main.go b/backend/cmd/main.go similarity index 67% rename from backend/main.go rename to backend/cmd/main.go index 1aaca45..bae7a83 100644 --- a/backend/main.go +++ b/backend/cmd/main.go @@ -1,34 +1,18 @@ package main import ( + "encoding/json" "fmt" - "os" - _ "ttime/docs" "ttime/internal/config" "ttime/internal/database" "ttime/internal/handlers" - "github.com/BurntSushi/toml" "github.com/gofiber/fiber/v2" - "github.com/gofiber/swagger" _ "github.com/mattn/go-sqlite3" jwtware "github.com/gofiber/contrib/jwt" ) -// @title TTime API -// @version 0.0.1 -// @description This is the API for TTime, a time tracking application. - -// @license.name AGPL -// @license.url https://www.gnu.org/licenses/agpl-3.0.html - -// @host localhost:8080 -// @BasePath /api - -// @externalDocs.description OpenAPI -// @externalDocs.url https://swagger.io/resources/open-api/ - func main() { conf, err := config.ReadConfigFromFile("config.toml") if err != nil { @@ -36,11 +20,9 @@ func main() { _ = conf.WriteConfigToFile("config.toml") } - // Pretty print the current config with toml - _ = toml.NewEncoder(os.Stdout).Encode(conf) - - fmt.Printf("Starting server on http://localhost:%d\n", conf.Port) - fmt.Printf("For documentation, go to http://localhost:%d/swagger/index.html\n", conf.Port) + // Pretty print the current config + str, _ := json.MarshalIndent(conf, "", " ") + fmt.Println(string(str)) // Connect to the database db := database.DbConnect(conf.DbPath) @@ -49,8 +31,6 @@ func main() { // Create the server server := fiber.New() - server.Get("/swagger/*", swagger.HandlerDefault) - // Mount our static files (Beware of the security implications of this!) // This will likely be replaced by an embedded filesystem in the future server.Static("/", "./static") diff --git a/backend/docs/docs.go b/backend/docs/docs.go deleted file mode 100644 index 8026f58..0000000 --- a/backend/docs/docs.go +++ /dev/null @@ -1,80 +0,0 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT -package docs - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "contact": {}, - "license": { - "name": "AGPL", - "url": "https://www.gnu.org/licenses/agpl-3.0.html" - }, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": { - "/api/register": { - "post": { - "description": "Register a new user", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "User" - ], - "summary": "Register a new user", - "responses": { - "200": { - "description": "User added", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad request", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - } - }, - "externalDocs": { - "description": "OpenAPI", - "url": "https://swagger.io/resources/open-api/" - } -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "0.0.1", - Host: "localhost:8080", - BasePath: "/api", - Schemes: []string{}, - Title: "TTime API", - Description: "This is the API for TTime, a time tracking application.", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/backend/go.mod b/backend/go.mod index c251e95..ab9be66 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -8,36 +8,10 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 ) -require ( - github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.2.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect - github.com/go-openapi/jsonpointer v0.20.3 // indirect - github.com/go-openapi/jsonreference v0.20.5 // indirect - github.com/go-openapi/spec v0.20.15 // indirect - github.com/go-openapi/swag v0.22.10 // indirect - github.com/gofiber/swagger v1.0.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/swaggo/files/v2 v2.0.0 // indirect - github.com/swaggo/swag v1.16.3 // indirect - github.com/urfave/cli/v2 v2.27.1 // indirect - github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.19.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) - require ( github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect - github.com/gofiber/contrib/jwt v1.0.8 - github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/gofiber/contrib/jwt v1.0.8 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect ) // These are all for fiber diff --git a/backend/go.sum b/backend/go.sum index a2fdf1e..42908f6 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,28 +1,11 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= -github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= -github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28= -github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/jsonpointer v0.20.3 h1:jykzYWS/kyGtsHfRt6aV8JTB9pcQAXPIA7qlZ5aRlyk= -github.com/go-openapi/jsonpointer v0.20.3/go.mod h1:c7l0rjoouAuIxCm8v/JWKRgMjDG/+/7UBWsXMrv6PsM= -github.com/go-openapi/jsonreference v0.20.5 h1:hutI+cQI+HbSQaIGSfsBsYI0pHk+CATf8Fk5gCSj0yI= -github.com/go-openapi/jsonreference v0.20.5/go.mod h1:thAqAp31UABtI+FQGKAQfmv7DbFpKNUlva2UPCxKu2Y= -github.com/go-openapi/spec v0.20.15 h1:8bDcVxF607pTh9NpPwgsH4J5Uhh5mV5XoWnkurdiY+U= -github.com/go-openapi/spec v0.20.15/go.mod h1:o0upgqg5uYFG7O5mADrDVmSG3Wa6y6OLhwiCqQ+sTv4= -github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA= -github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gofiber/contrib/jwt v1.0.8 h1:/GeOsm/Mr1OGr0GTy+RIVSz5VgNNyP3ZgK4wdqxF/WY= @@ -31,28 +14,20 @@ github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlg github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/gofiber/fiber/v2 v2.52.2 h1:b0rYH6b06Df+4NyrbdptQL8ifuxw/Tf2DgfkZkDaxEo= github.com/gofiber/fiber/v2 v2.52.2/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc= -github.com/gofiber/swagger v1.0.0/go.mod h1:QrYNF1Yrc7ggGK6ATsJ6yfH/8Zi5bu9lA7wB8TmCecg= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -63,21 +38,10 @@ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= -github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= -github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= -github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= -github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= -github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= @@ -86,24 +50,9 @@ github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7g github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5a88873..6e86641 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -2,9 +2,9 @@ package database import ( "embed" + "log" "os" "path/filepath" - "time" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" @@ -12,20 +12,11 @@ import ( // Interface for the database type Database interface { - // Insert a new user into the database, password should be hashed before calling AddUser(username string, password string) error - RemoveUser(username string) error - PromoteToAdmin(username string) error GetUserId(username string) (int, error) AddProject(name string, description string, username string) error Migrate(dirname string) error - // AddTimeReport(projectname string, start time.Time, end time.Time) error - // AddUserToProject(username string, projectname string) error - // ChangeUserRole(username string, projectname string, role string) error - // AddTimeReport(projectname string, start time.Time, end time.Time) error - // AddUserToProject(username string, projectname string) error - // ChangeUserRole(username string, projectname string, role string) error } // This struct is a wrapper type that holds the database connection @@ -38,11 +29,7 @@ type Db struct { var scripts embed.FS const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" -const projectInsert = "INSERT INTO projects (name, description, owner_user_id) SELECT ?, ?, id FROM users WHERE username = ?" -const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?" -const addTimeReport = "INSERT INTO activity (report_id, activity_nbr, start_time, end_time, break, comment) VALUES (?, ?, ?, ?, ?, ?)" // WIP -const addUserToProject = "INSERT INTO project_member (project_id, user_id, role) VALUES (?, ?, ?)" // WIP -// const changeUserRole = "" +const projectInsert = "INSERT INTO projects (name, description, user_id) SELECT ?, ?, id FROM users WHERE username = ?" // DbConnect connects to the database func DbConnect(dbpath string) Database { @@ -61,32 +48,6 @@ func DbConnect(dbpath string) Database { return &Db{db} } -func (d *Db) AddTimeReport(projectname string, start time.Time, end time.Time, breakTime uint32) error { // WIP - _, err := d.Exec(addTimeReport, projectname, 0, start, end, breakTime, false) - return err -} - -func (d *Db) AddUserToProject(username string, projectname string, role string) error { // WIP - var userid int - userid, err := d.GetUserId(username) - if err != nil { - panic(err) - } - - var projectid int - projectid, err2 := d.GetProjectId(projectname) - if err2 != nil { - panic(err2) - } - - _, err3 := d.Exec(addUserToProject, projectid, userid, role) - return err3 -} - -// func (d *Db) ChangeUserRole(username string, projectname string, role string) error { - -// } - // AddUser adds a user to the database func (d *Db) AddUser(username string, password string) error { _, err := d.Exec(userInsert, username, password) @@ -99,20 +60,9 @@ func (d *Db) RemoveUser(username string) error { return err } -func (d *Db) PromoteToAdmin(username string) error { - _, err := d.Exec(promoteToAdmin, username) - return err -} - func (d *Db) GetUserId(username string) (int, error) { var id int - err := d.Get(&id, "SELECT id FROM users WHERE username = ?", username) // Borde det inte vara "user" i singular - return id, err -} - -func (d *Db) GetProjectId(projectname string) (int, error) { // WIP, denna kan vara goof - var id int - err := d.Get(&id, "SELECT id FROM project WHERE project_name = ?", projectname) + err := d.Get(&id, "SELECT id FROM users WHERE username = ?", username) return id, err } @@ -151,6 +101,7 @@ func (d *Db) Migrate(dirname string) error { if err != nil { return err } + log.Println("Executed SQL file:", file.Name()) } if tr.Commit() != nil { diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 96eb9b7..6830668 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -74,32 +74,3 @@ func TestDbRemoveUser(t *testing.T) { t.Error("RemoveUser failed:", err) } } - -func TestPromoteToAdmin(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("test", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.PromoteToAdmin("test") - if err != nil { - t.Error("PromoteToAdmin failed:", err) - } -} - -// func TestAddTimeReport(t *testing.T) { - -// } - -// func TestAddUserToProject(t *testing.T) { - -// } - -// func TestChangeUserRole(t *testing.T) { - -// } diff --git a/backend/internal/database/migrations/0010_users.sql b/backend/internal/database/migrations/0010_users.sql index 5c9d329..7cb23fd 100644 --- a/backend/internal/database/migrations/0010_users.sql +++ b/backend/internal/database/migrations/0010_users.sql @@ -1,7 +1,3 @@ --- Id is a surrogate key for in ternal use --- userId is what is used for external id --- username is what is used for login --- password is the hashed password CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, userId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE, @@ -9,6 +5,5 @@ CREATE TABLE IF NOT EXISTS users ( password VARCHAR(255) NOT NULL ); --- Users are commonly searched by username and userId CREATE INDEX IF NOT EXISTS users_username_index ON users (username); CREATE INDEX IF NOT EXISTS users_userId_index ON users (userId); \ No newline at end of file diff --git a/backend/internal/database/migrations/0020_projects.sql b/backend/internal/database/migrations/0020_projects.sql index adfb818..8592e75 100644 --- a/backend/internal/database/migrations/0020_projects.sql +++ b/backend/internal/database/migrations/0020_projects.sql @@ -3,9 +3,9 @@ CREATE TABLE IF NOT EXISTS projects ( projectId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL UNIQUE, description TEXT NOT NULL, - owner_user_id INTEGER NOT NULL, - FOREIGN KEY (owner_user_id) REFERENCES users (id) + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) ); CREATE INDEX IF NOT EXISTS projects_projectId_index ON projects (projectId); -CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (owner_user_id); \ No newline at end of file +CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (user_id); \ No newline at end of file diff --git a/backend/internal/database/migrations/0049_project_role.sql b/backend/internal/database/migrations/0049_project_role.sql deleted file mode 100644 index 8716800..0000000 --- a/backend/internal/database/migrations/0049_project_role.sql +++ /dev/null @@ -1,9 +0,0 @@ --- This table represents the possible role a user can have in a project. --- It has nothing to do with the site admin table. -CREATE TABLE IF NOT EXISTS project_role ( - p_role TEXT PRIMARY KEY -); - --- Insert the possible roles a user can have in a project. -INSERT OR IGNORE INTO project_role (p_role) VALUES ('admin'); -INSERT OR IGNORE INTO project_role (p_role) VALUES ('member'); diff --git a/backend/internal/database/migrations/0050_user_roles.sql b/backend/internal/database/migrations/0050_user_roles.sql index aad25f7..56e597b 100644 --- a/backend/internal/database/migrations/0050_user_roles.sql +++ b/backend/internal/database/migrations/0050_user_roles.sql @@ -1,9 +1,16 @@ CREATE TABLE IF NOT EXISTS user_roles ( user_id INTEGER NOT NULL, project_id INTEGER NOT NULL, - p_role TEXT NOT NULL, -- 'admin' or 'member' + role STRING NOT NULL, -- 'admin' or 'member' FOREIGN KEY (user_id) REFERENCES users (id) FOREIGN KEY (project_id) REFERENCES projects (id) - FOREIGN KEY (p_role) REFERENCES project_role (p_role) PRIMARY KEY (user_id, project_id) -); \ No newline at end of file +); + +-- Make sure that the role is either 'admin' or 'member' +CREATE TRIGGER IF NOT EXISTS user_role_admin_or_member + BEFORE INSERT ON user_roles + FOR EACH ROW + BEGIN + SELECT RAISE(ABORT, 'Invalid role') WHERE NEW.role NOT IN ('admin', 'member'); + END; \ No newline at end of file diff --git a/backend/internal/database/migrations/0060_site_admin.sql b/backend/internal/database/migrations/0060_site_admin.sql deleted file mode 100644 index 14de60d..0000000 --- a/backend/internal/database/migrations/0060_site_admin.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE IF NOT EXISTS site_admin ( - admin_id INTEGER PRIMARY KEY, - FOREIGN KEY (admin_id) REFERENCES users (id) ON DELETE CASCADE -) \ No newline at end of file diff --git a/backend/internal/database/migrations/0070_salts.sql b/backend/internal/database/migrations/0070_salts.sql deleted file mode 100644 index b84dfac..0000000 --- a/backend/internal/database/migrations/0070_salts.sql +++ /dev/null @@ -1,16 +0,0 @@ --- It is unclear weather this table will be used - --- Create the table to store hash salts -CREATE TABLE salts ( - id INTEGER PRIMARY KEY, - salt TEXT NOT NULL -); - --- Commented out for now, no time for good practices, which is atrocious --- Create a trigger to automatically generate a salt when inserting a new user record --- CREATE TRIGGER generate_salt_trigger --- AFTER INSERT ON users --- BEGIN --- INSERT INTO salts (salt) VALUES (randomblob(16)); --- UPDATE users SET salt_id = (SELECT last_insert_rowid()) WHERE id = new.id; --- END; diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 9c42133..5dc8895 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -11,11 +11,11 @@ import ( // The actual interface that we will use type GlobalState interface { - Register(c *fiber.Ctx) error // To register a new user - UserDelete(c *fiber.Ctx) error // To delete a user - Login(c *fiber.Ctx) error // To get the token - LoginRenew(c *fiber.Ctx) error // To renew the token - CreateProject(c *fiber.Ctx) error // To create a new project + Register(c *fiber.Ctx) error // To register a new user + UserDelete(c *fiber.Ctx) error // To delete a user + Login(c *fiber.Ctx) error // To get the token + LoginRenew(c *fiber.Ctx) error // To renew the token + // CreateProject(c *fiber.Ctx) error // To create a new project // GetProjects(c *fiber.Ctx) error // To get all projects // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project @@ -46,19 +46,8 @@ type GState struct { ButtonCount int } -// Register is a simple handler that registers a new user -// -// @Summary Register a new user -// @Description Register a new user -// @Tags User -// @Accept json -// @Produce json -// @Success 200 {string} string "User added" -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" -// @Router /api/register [post] func (gs *GState) Register(c *fiber.Ctx) error { - u := new(types.NewUser) + u := new(types.User) if err := c.BodyParser(u); err != nil { return c.Status(400).SendString(err.Error()) } @@ -126,7 +115,6 @@ func (gs *GState) Login(c *fiber.Ctx) error { // LoginRenew is a simple handler that renews the token func (gs *GState) LoginRenew(c *fiber.Ctx) error { - // For testing: curl localhost:3000/restricted -H "Authorization: Bearer " user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) claims["exp"] = time.Now().Add(time.Hour * 72).Unix() @@ -142,24 +130,3 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { } return c.JSON(fiber.Map{"token": t}) } - -// CreateProject is a simple handler that creates a new project -func (gs *GState) CreateProject(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - - p := new(types.NewProject) - if err := c.BodyParser(p); err != nil { - return c.Status(400).SendString(err.Error()) - } - - // Get the username from the token and set it as the owner of the project - // This is ugly but - claims := user.Claims.(jwt.MapClaims) - p.Owner = claims["name"].(string) - - if err := gs.Db.AddProject(p.Name, p.Description, p.Owner); err != nil { - return c.Status(500).SendString(err.Error()) - } - - return c.Status(200).SendString("Project added") -} diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go deleted file mode 100644 index cabf6c6..0000000 --- a/backend/internal/types/project.go +++ /dev/null @@ -1,21 +0,0 @@ -package types - -import ( - "time" -) - -// Project is a struct that holds the information about a project -type Project struct { - ID int `json:"id" db:"id"` - Name string `json:"name" db:"name"` - Description string `json:"description" db:"description"` - Owner string `json:"owner" db:"owner"` - Created time.Time `json:"created" db:"created"` -} - -// As it arrives from the client -type NewProject struct { - Name string `json:"name"` - Description string `json:"description"` - Owner string `json:"owner"` -} diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go index 233ec71..fa735d7 100644 --- a/backend/internal/types/users.go +++ b/backend/internal/types/users.go @@ -16,11 +16,6 @@ func (u *User) ToPublicUser() (*PublicUser, error) { }, nil } -type NewUser struct { - Username string `json:"username"` - Password string `json:"password"` -} - // PublicUser represents a user that is safe to send over the API (no password) type PublicUser struct { UserId string `json:"userId"` diff --git a/frontend/src/Pages/AdminPages/AdminMenuPage.tsx b/frontend/src/Pages/AdminPages/AdminMenuPage.tsx deleted file mode 100644 index 1b32ed4..0000000 --- a/frontend/src/Pages/AdminPages/AdminMenuPage.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Link } from "react-router-dom"; -import BasicWindow from "../../Components/BasicWindow"; - -function AdminMenuPage(): JSX.Element { - const content = ( - <> -

Administrator Menu

-
- -

- Manage Users -

- - -

- Manage Projects -

- -
- - ); - - const buttons = <>; - - return ; -} -export default AdminMenuPage; diff --git a/frontend/src/Pages/Home.tsx b/frontend/src/Pages/Home.tsx new file mode 100644 index 0000000..7ce73a6 --- /dev/null +++ b/frontend/src/Pages/Home.tsx @@ -0,0 +1,36 @@ +import reactLogo from "../assets/react.svg"; +import viteLogo from "/vite.svg"; +import "../index.css"; +import { CountButton } from "../Components/CountButton"; +import { Link } from "react-router-dom"; + +/** + * The home page of the application + * @returns {JSX.Element} The home page + */ +export default function HomePage(): JSX.Element { + return ( + <> +
+ + Vite logo + + + React logo + +
+

Vite + React

+
+ + To Settings +
+

+ Click on the Vite and React logos to learn more +

+ + ); +} diff --git a/frontend/src/Pages/Settings.tsx b/frontend/src/Pages/Settings.tsx new file mode 100644 index 0000000..b5bf81c --- /dev/null +++ b/frontend/src/Pages/Settings.tsx @@ -0,0 +1,17 @@ +import "../index.css"; +import { Link } from "react-router-dom"; + +/** + * The settings page of the application + * @returns {JSX.Element} The settings page + */ +export default function SettingsPage(): JSX.Element { + return ( + <> +

Very Fancy Settings Page

+
+ To Home +
+ + ); +} diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 03091a2..d5616c7 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -5,7 +5,6 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; import LoginPage from "./Pages/LoginPage.tsx"; import YourProjectsPage from "./Pages/YourProjectsPage.tsx"; import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx"; -import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx"; // This is where the routes are mounted const router = createBrowserRouter([ @@ -21,10 +20,6 @@ const router = createBrowserRouter([ path: "/project", element: , }, - { - path: "/admin-menu", - element: , - }, ]); // Semi-hacky way to get the root element diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index a58340b..0000000 --- a/go.work.sum +++ /dev/null @@ -1,15 +0,0 @@ -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=