Merge remote-tracking branch 'origin/frontend' into gruppPP
This commit is contained in:
commit
f0fc465d1a
21 changed files with 721 additions and 112 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -6,8 +6,13 @@
|
||||||
*.dylib
|
*.dylib
|
||||||
|
|
||||||
bin
|
bin
|
||||||
|
database.txt
|
||||||
|
plantuml.jar
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
*.png
|
diagram.puml
|
||||||
|
backend/*.png
|
||||||
|
backend/*.jpg
|
||||||
|
backend/*.svg
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
49
README.md
49
README.md
|
@ -23,12 +23,16 @@ Dependencies:
|
||||||
- [Podman](https://podman.io/) / [Docker](https://www.docker.com/)
|
- [Podman](https://podman.io/) / [Docker](https://www.docker.com/)
|
||||||
- [Just](https://github.com/casey/just) (Optional)
|
- [Just](https://github.com/casey/just) (Optional)
|
||||||
|
|
||||||
|
### Fedora/Red Hat
|
||||||
|
|
||||||
If you're on [Fedora](https://fedoraproject.org/)/Red Hat derivatives, this is as simple as:
|
If you're on [Fedora](https://fedoraproject.org/)/Red Hat derivatives, this is as simple as:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y make golang nodejs podman just
|
sudo dnf install -y make golang nodejs podman just
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Debian/Ubuntu
|
||||||
|
|
||||||
Any [Debian](https://www.debian.org/)/[Ubuntu](https://ubuntu.com/desktop)-based distro:
|
Any [Debian](https://www.debian.org/)/[Ubuntu](https://ubuntu.com/desktop)-based distro:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -36,19 +40,43 @@ sudo apt install -y make golang nodejs podman
|
||||||
sudo apt install -y just # For Ubuntu
|
sudo apt install -y just # For Ubuntu
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Arch Linux
|
||||||
|
|
||||||
[Arch Linux](https://archlinux.org/) & derivatives:
|
[Arch Linux](https://archlinux.org/) & derivatives:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S make go nodejs npm podman just
|
sudo pacman -S make go nodejs npm podman just
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
[MacOS](https://www.apple.com/macos/): (Requires [Homebrew](https://brew.sh/)) (Untested)
|
[MacOS](https://www.apple.com/macos/): (Requires [Homebrew](https://brew.sh/)) (Untested)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install make go nodejs npm podman just
|
brew install make go nodejs npm podman just
|
||||||
```
|
```
|
||||||
|
|
||||||
[Windows](https://www.microsoft.com/en-us/windows):
|
### Windows
|
||||||
|
|
||||||
|
#### Vanilla Windows
|
||||||
|
|
||||||
|
The project should now build properly on Windows, given the dependencies:
|
||||||
|
|
||||||
|
- [Go](https://go.dev/)
|
||||||
|
- [Node & npm](https://nodejs.org/en/)
|
||||||
|
|
||||||
|
With chocolatey, you can install these dependencies with the following commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
choco install -y golang nodejs
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that none of the convenience tools (Make, Podman, Just*) are available on Windows.
|
||||||
|
|
||||||
|
*Just is available, but the targets are written for a Unix-like environment.
|
||||||
|
|
||||||
|
#### Windows Subsystem for Linux (WSL)
|
||||||
|
|
||||||
Unfortunately, [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install) is required for the build process. Running any form of containerized workload on windows is currently unsupported. More info [here](https://podman.io/docs/installation#windows). From my understanding, WSL also requires virtualization extensions to be enabled in the BIOS, which is not always the case for all users.
|
Unfortunately, [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install) is required for the build process. Running any form of containerized workload on windows is currently unsupported. More info [here](https://podman.io/docs/installation#windows). From my understanding, WSL also requires virtualization extensions to be enabled in the BIOS, which is not always the case for all users.
|
||||||
|
|
||||||
It is possible to run the code on Windows, but this will be without the use of containers or any other build tools that are not available on Windows.
|
It is possible to run the code on Windows, but this will be without the use of containers or any other build tools that are not available on Windows.
|
||||||
|
@ -62,12 +90,19 @@ You should consult the [WSL documentation](https://docs.microsoft.com/en-us/wind
|
||||||
wsl --install -d Ubuntu-22.04 # To get a somewhat recent version of Go
|
wsl --install -d Ubuntu-22.04 # To get a somewhat recent version of Go
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
After this, you can open a (wsl) terminal and run the commands:
|
||||||
|
|
||||||
If you're **still dead set** on using a vanilla Windows environment, you will need the following:
|
```bash
|
||||||
|
sudo apt update && sudo apt upgrade
|
||||||
|
sudo apt install -y make podman
|
||||||
|
|
||||||
- [Go](https://go.dev/)
|
sudo add-apt-repository ppa:longsleep/golang-backports
|
||||||
- [Node & npm](https://nodejs.org/en/)
|
sudo apt update
|
||||||
- [MariaDB](https://mariadb.org/) / [MySQL](https://www.mysql.com/) / [PostgreSQL](https://www.postgresql.org/) (This is undecided so far)
|
sudo apt install golang-go
|
||||||
|
|
||||||
With some grit and determination, you can get it to work. It's not recommended, but I (Imbus) will try to help you.
|
# For a recent version of node:
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
||||||
|
nvm install node
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
@ -27,6 +27,10 @@ clean:
|
||||||
$(GOCLEAN)
|
$(GOCLEAN)
|
||||||
rm -rf bin
|
rm -rf bin
|
||||||
rm -f db.sqlite3
|
rm -f db.sqlite3
|
||||||
|
rm -f diagram*
|
||||||
|
rm -f plantuml.jar
|
||||||
|
rm -f erd.png
|
||||||
|
rm -f config.toml
|
||||||
|
|
||||||
# Test target
|
# Test target
|
||||||
test: db.sqlite3
|
test: db.sqlite3
|
||||||
|
@ -54,6 +58,9 @@ migrate:
|
||||||
db.sqlite3:
|
db.sqlite3:
|
||||||
make migrate
|
make migrate
|
||||||
|
|
||||||
|
dbdump:
|
||||||
|
sqlite3 $(DB_FILE) .dump > database.txt
|
||||||
|
|
||||||
backup:
|
backup:
|
||||||
mkdir -p backups
|
mkdir -p backups
|
||||||
sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz
|
sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz
|
||||||
|
@ -95,6 +102,18 @@ install-lint:
|
||||||
@echo "Installing golangci-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
|
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.42.1
|
||||||
|
|
||||||
|
# Fetches the latest plantuml.jar and checks its SHA256 hash
|
||||||
|
plantuml.jar:
|
||||||
|
curl -sSfL https://github.com/plantuml/plantuml/releases/download/v1.2024.3/plantuml.jar -o plantuml.jar \
|
||||||
|
&& echo "519a4a7284c6a0357c369e4bb0caf72c4bfbbde851b8c6d6bbdb7af3c01fc82f plantuml.jar" | sha256sum -c
|
||||||
|
|
||||||
|
# Generate UML diagrams diagral.png & diagram.svg
|
||||||
|
.PHONY: uml
|
||||||
|
uml: plantuml.jar
|
||||||
|
goplantuml -recursive . > diagram.puml
|
||||||
|
java -jar plantuml.jar -tpng diagram.puml
|
||||||
|
java -jar plantuml.jar -tsvg diagram.puml
|
||||||
|
|
||||||
# Convenience target to install just (requires sudo privileges)
|
# Convenience target to install just (requires sudo privileges)
|
||||||
install-just:
|
install-just:
|
||||||
@echo "Installing just"
|
@echo "Installing just"
|
||||||
|
|
|
@ -4,34 +4,33 @@ go 1.21.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.3.2
|
github.com/BurntSushi/toml v1.3.2
|
||||||
|
github.com/gofiber/swagger v1.0.0
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/swaggo/swag v1.16.3
|
||||||
|
modernc.org/sqlite v1.29.5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.2.1 // indirect
|
github.com/dustin/go-humanize v1.0.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/jsonpointer v0.20.3 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.5 // indirect
|
github.com/go-openapi/jsonreference v0.20.5 // indirect
|
||||||
github.com/go-openapi/spec v0.20.15 // indirect
|
github.com/go-openapi/spec v0.20.15 // indirect
|
||||||
github.com/go-openapi/swag v0.22.10 // indirect
|
github.com/go-openapi/swag v0.22.10 // indirect
|
||||||
github.com/gofiber/swagger v1.0.0 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/swaggo/files/v2 v2.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
|
golang.org/x/tools v0.19.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
|
modernc.org/libc v1.41.0 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/memory v1.7.2 // indirect
|
||||||
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
|
modernc.org/token v1.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
|
@ -4,17 +4,12 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
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 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k=
|
||||||
github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k=
|
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 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/go-openapi/jsonpointer v0.20.3 h1:jykzYWS/kyGtsHfRt6aV8JTB9pcQAXPIA7qlZ5aRlyk=
|
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/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 h1:hutI+cQI+HbSQaIGSfsBsYI0pHk+CATf8Fk5gCSj0yI=
|
||||||
|
@ -27,27 +22,27 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
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=
|
github.com/gofiber/contrib/jwt v1.0.8 h1:/GeOsm/Mr1OGr0GTy+RIVSz5VgNNyP3ZgK4wdqxF/WY=
|
||||||
github.com/gofiber/contrib/jwt v1.0.8/go.mod h1:gWWBtBiLmKXRN7xy6a96QO0KGvPEyxdh8x496Ujtg84=
|
github.com/gofiber/contrib/jwt v1.0.8/go.mod h1:gWWBtBiLmKXRN7xy6a96QO0KGvPEyxdh8x496Ujtg84=
|
||||||
github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI=
|
|
||||||
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 h1:b0rYH6b06Df+4NyrbdptQL8ifuxw/Tf2DgfkZkDaxEo=
|
||||||
github.com/gofiber/fiber/v2 v2.52.2/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
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 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc=
|
||||||
github.com/gofiber/swagger v1.0.0/go.mod h1:QrYNF1Yrc7ggGK6ATsJ6yfH/8Zi5bu9lA7wB8TmCecg=
|
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 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
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/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
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 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
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 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
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 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
@ -63,47 +58,55 @@ 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.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 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
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 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
|
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/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 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
||||||
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
|
||||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
|
||||||
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
||||||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
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 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
|
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
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.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.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 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
|
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||||
|
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||||
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
|
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||||
|
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||||
|
modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE=
|
||||||
|
modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
|
||||||
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
|
|
@ -5,27 +5,30 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
"ttime/internal/types"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Interface for the database
|
// Interface for the database
|
||||||
type Database interface {
|
type Database interface {
|
||||||
// Insert a new user into the database, password should be hashed before calling
|
// Insert a new user into the database, password should be hashed before calling
|
||||||
AddUser(username string, password string) error
|
AddUser(username string, password string) error
|
||||||
|
|
||||||
RemoveUser(username string) error
|
RemoveUser(username string) error
|
||||||
PromoteToAdmin(username string) error
|
PromoteToAdmin(username string) error
|
||||||
GetUserId(username string) (int, error)
|
GetUserId(username string) (int, error)
|
||||||
AddProject(name string, description string, username string) error
|
AddProject(name string, description string, username string) error
|
||||||
Migrate(dirname string) error
|
Migrate(dirname string) error
|
||||||
// AddTimeReport(projectname string, start time.Time, end time.Time) error
|
GetProjectId(projectname string) (int, error)
|
||||||
// AddUserToProject(username string, projectname string) error
|
AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error
|
||||||
// ChangeUserRole(username string, projectname string, role string) error
|
AddUserToProject(username string, projectname string, role string) error
|
||||||
// AddTimeReport(projectname string, start time.Time, end time.Time) error
|
ChangeUserRole(username string, projectname string, role string) error
|
||||||
// AddUserToProject(username string, projectname string) error
|
GetAllUsersProject(projectname string) ([]UserProjectMember, error)
|
||||||
// ChangeUserRole(username string, projectname string, role string) error
|
GetAllUsersApplication() ([]string, error)
|
||||||
|
GetProjectsForUser(username string) ([]types.Project, error)
|
||||||
|
GetAllProjects() ([]types.Project, error)
|
||||||
|
GetUserRole(username string, projectname string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This struct is a wrapper type that holds the database connection
|
// This struct is a wrapper type that holds the database connection
|
||||||
|
@ -34,20 +37,44 @@ type Db struct {
|
||||||
*sqlx.DB
|
*sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserProjectMember struct {
|
||||||
|
Username string `db:"username"`
|
||||||
|
UserRole string `db:"p_role"`
|
||||||
|
}
|
||||||
|
|
||||||
//go:embed migrations
|
//go:embed migrations
|
||||||
var scripts embed.FS
|
var scripts embed.FS
|
||||||
|
|
||||||
|
// TODO: Possibly break these out into separate files bundled with the embed package?
|
||||||
const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)"
|
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 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 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 addTimeReport = `WITH UserLookup AS (SELECT id FROM users WHERE username = ?),
|
||||||
const addUserToProject = "INSERT INTO project_member (project_id, user_id, role) VALUES (?, ?, ?)" // WIP
|
ProjectLookup AS (SELECT id FROM projects WHERE name = ?)
|
||||||
// const changeUserRole = ""
|
INSERT INTO time_reports (project_id, user_id, start, end)
|
||||||
|
VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup), ?, ?);`
|
||||||
|
const addUserToProject = "INSERT INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)" // WIP
|
||||||
|
const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = ? AND project_id = ?"
|
||||||
|
|
||||||
|
const getProjectsForUser = `
|
||||||
|
SELECT
|
||||||
|
projects.id,
|
||||||
|
projects.name,
|
||||||
|
projects.description,
|
||||||
|
projects.owner_user_id
|
||||||
|
FROM
|
||||||
|
projects
|
||||||
|
JOIN
|
||||||
|
user_roles ON projects.id = user_roles.project_id
|
||||||
|
JOIN
|
||||||
|
users ON user_roles.user_id = users.id
|
||||||
|
WHERE
|
||||||
|
users.username = ?;`
|
||||||
|
|
||||||
// DbConnect connects to the database
|
// DbConnect connects to the database
|
||||||
func DbConnect(dbpath string) Database {
|
func DbConnect(dbpath string) Database {
|
||||||
// Open the database
|
// Open the database
|
||||||
db, err := sqlx.Connect("sqlite3", dbpath)
|
db, err := sqlx.Connect("sqlite", dbpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -61,8 +88,20 @@ func DbConnect(dbpath string) Database {
|
||||||
return &Db{db}
|
return &Db{db}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Db) AddTimeReport(projectname string, start time.Time, end time.Time, breakTime uint32) error { // WIP
|
func (d *Db) GetProjectsForUser(username string) ([]types.Project, error) {
|
||||||
_, err := d.Exec(addTimeReport, projectname, 0, start, end, breakTime, false)
|
var projects []types.Project
|
||||||
|
err := d.Select(&projects, getProjectsForUser, username)
|
||||||
|
return projects, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Db) GetAllProjects() ([]types.Project, error) {
|
||||||
|
var projects []types.Project
|
||||||
|
err := d.Select(&projects, "SELECT * FROM projects")
|
||||||
|
return projects, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Db) AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error { // WIP
|
||||||
|
_, err := d.Exec(addTimeReport, userName, projectName, start, end)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,13 +118,32 @@ func (d *Db) AddUserToProject(username string, projectname string, role string)
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err3 := d.Exec(addUserToProject, projectid, userid, role)
|
_, err3 := d.Exec(addUserToProject, userid, projectid, role)
|
||||||
return err3
|
return err3
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
|
func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
|
||||||
|
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(changeUserRole, role, userid, projectid)
|
||||||
|
return err3
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Db) GetUserRole(username string, projectname string) (string, error) {
|
||||||
|
var role string
|
||||||
|
err := d.Get(&role, "SELECT p_role FROM user_roles WHERE user_id = (SELECT id FROM users WHERE username = ?) AND project_id = (SELECT id FROM projects WHERE name = ?)", username, projectname)
|
||||||
|
return role, err
|
||||||
|
}
|
||||||
|
|
||||||
// AddUser adds a user to the database
|
// AddUser adds a user to the database
|
||||||
func (d *Db) AddUser(username string, password string) error {
|
func (d *Db) AddUser(username string, password string) error {
|
||||||
|
@ -110,9 +168,9 @@ func (d *Db) GetUserId(username string) (int, error) {
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Db) GetProjectId(projectname string) (int, error) { // WIP, denna kan vara goof
|
func (d *Db) GetProjectId(projectname string) (int, error) {
|
||||||
var id int
|
var id int
|
||||||
err := d.Get(&id, "SELECT id FROM project WHERE project_name = ?", projectname)
|
err := d.Get(&id, "SELECT id FROM projects WHERE name = ?", projectname)
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +180,69 @@ func (d *Db) AddProject(name string, description string, username string) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) {
|
||||||
|
// Define the SQL query to fetch users and their roles for a given project
|
||||||
|
query := `
|
||||||
|
SELECT u.username, ur.p_role
|
||||||
|
FROM users u
|
||||||
|
INNER JOIN user_roles ur ON u.id = ur.user_id
|
||||||
|
INNER JOIN projects p ON ur.project_id = p.id
|
||||||
|
WHERE p.name = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
|
rows, err := d.Queryx(query, projectname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
// Iterate over the rows and populate the result slice
|
||||||
|
var users []UserProjectMember
|
||||||
|
for rows.Next() {
|
||||||
|
var user UserProjectMember
|
||||||
|
if err := rows.StructScan(&user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllUsersApplication retrieves all usernames from the database
|
||||||
|
func (d *Db) GetAllUsersApplication() ([]string, error) {
|
||||||
|
// Define the SQL query to fetch all usernames
|
||||||
|
query := `
|
||||||
|
SELECT username FROM users
|
||||||
|
`
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
|
rows, err := d.Queryx(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
// Iterate over the rows and populate the result slice
|
||||||
|
var usernames []string
|
||||||
|
for rows.Next() {
|
||||||
|
var username string
|
||||||
|
if err := rows.Scan(&username); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
usernames = append(usernames, username)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return usernames, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Reads a directory of migration files and applies them to the database.
|
// Reads a directory of migration files and applies them to the database.
|
||||||
// This will eventually be used on an embedded directory
|
// This will eventually be used on an embedded directory
|
||||||
func (d *Db) Migrate(dirname string) error {
|
func (d *Db) Migrate(dirname string) error {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests are not guaranteed to be sequential
|
// Tests are not guaranteed to be sequential
|
||||||
|
@ -92,14 +93,253 @@ func TestPromoteToAdmin(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestAddTimeReport(t *testing.T) {
|
func TestAddTimeReport(t *testing.T) {
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
err = db.AddUser("testuser", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
// func TestAddUserToProject(t *testing.T) {
|
err = db.AddProject("testproject", "description", "testuser")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
var now = time.Now()
|
||||||
|
var then = now.Add(time.Hour)
|
||||||
|
|
||||||
// func TestChangeUserRole(t *testing.T) {
|
err = db.AddTimeReport("testproject", "testuser", now, then)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddTimeReport failed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
func TestAddUserToProject(t *testing.T) {
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddProject("testproject", "description", "testuser")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = time.Now()
|
||||||
|
var then = now.Add(time.Hour)
|
||||||
|
|
||||||
|
err = db.AddTimeReport("testproject", "testuser", now, then)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddTimeReport failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUserToProject("testuser", "testproject", "user")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUserToProject failed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangeUserRole(t *testing.T) {
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddProject("testproject", "description", "testuser")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUserToProject("testuser", "testproject", "user")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUserToProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err := db.GetUserRole("testuser", "testproject")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetUserRole failed:", err)
|
||||||
|
}
|
||||||
|
if role != "user" {
|
||||||
|
t.Error("GetUserRole failed: expected user, got", role)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.ChangeUserRole("testuser", "testproject", "admin")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("ChangeUserRole failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err = db.GetUserRole("testuser", "testproject")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetUserRole failed:", err)
|
||||||
|
}
|
||||||
|
if role != "admin" {
|
||||||
|
t.Error("GetUserRole failed: expected admin, got", role)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllUsersProject(t *testing.T) {
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser1", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser2", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddProject("testproject", "description", "testuser1")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUserToProject("testuser1", "testproject", "project_manager")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUserToProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUserToProject("testuser2", "testproject", "user")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUserToProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := db.GetAllUsersProject("testproject")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetAllUsersProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if both users are returned with their roles
|
||||||
|
if len(users) != 2 {
|
||||||
|
t.Errorf("Expected 2 users, got %d", len(users))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if testuser1 has project manager role
|
||||||
|
foundProjectManager := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Username == "testuser1" && user.UserRole == "project_manager" {
|
||||||
|
foundProjectManager = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundProjectManager {
|
||||||
|
t.Error("Project Manager user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if testuser2 has user role
|
||||||
|
foundUser := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Username == "testuser2" && user.UserRole == "user" {
|
||||||
|
foundUser = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundUser {
|
||||||
|
t.Error("User user not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllUsersApplication(t *testing.T) {
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser1", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser2", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := db.GetAllUsersApplication()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetAllUsersApplication failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if both users are returned
|
||||||
|
if len(users) != 2 {
|
||||||
|
t.Errorf("Expected 2 users, got %d", len(users))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the test users are included in the list
|
||||||
|
foundTestUser1 := false
|
||||||
|
foundTestUser2 := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user == "testuser1" {
|
||||||
|
foundTestUser1 = true
|
||||||
|
}
|
||||||
|
if user == "testuser2" {
|
||||||
|
foundTestUser2 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundTestUser1 {
|
||||||
|
t.Error("testuser1 not found")
|
||||||
|
}
|
||||||
|
if !foundTestUser2 {
|
||||||
|
t.Error("testuser2 not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetProjectsForUser(t *testing.T) {
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUser("testuser", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddProject("testproject", "description", "testuser")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.AddUserToProject("testuser", "testproject", "user")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUserToProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
projects1, err := db.GetAllProjects()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetAllProjects failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(projects1) != 1 {
|
||||||
|
t.Error("GetAllProjects failed: expected 1, got", len(projects1))
|
||||||
|
}
|
||||||
|
|
||||||
|
projects, err := db.GetProjectsForUser("testuser")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetProjectsForUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(projects) != 1 {
|
||||||
|
t.Error("GetProjectsForUser failed: expected 1, got", len(projects))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
CREATE TABLE IF NOT EXISTS projects (
|
CREATE TABLE IF NOT EXISTS projects (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
projectId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE,
|
|
||||||
name VARCHAR(255) NOT NULL UNIQUE,
|
name VARCHAR(255) NOT NULL UNIQUE,
|
||||||
description TEXT NOT NULL,
|
description TEXT NOT NULL,
|
||||||
owner_user_id INTEGER NOT NULL,
|
owner_user_id INTEGER NOT NULL,
|
||||||
FOREIGN KEY (owner_user_id) REFERENCES users (id)
|
FOREIGN KEY (owner_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);
|
CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (owner_user_id);
|
|
@ -1,10 +1,11 @@
|
||||||
CREATE TABLE IF NOT EXISTS time_reports (
|
CREATE TABLE IF NOT EXISTS time_reports (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
reportId TEXT DEFAULT (HEX(RANDOMBLOB(6))) NOT NULL UNIQUE,
|
|
||||||
project_id INTEGER NOT NULL,
|
project_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
start DATETIME NOT NULL,
|
start DATETIME NOT NULL,
|
||||||
end DATETIME NOT NULL,
|
end DATETIME NOT NULL,
|
||||||
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
|
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end
|
CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end
|
||||||
|
|
|
@ -5,5 +5,5 @@ CREATE TABLE IF NOT EXISTS project_role (
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Insert the possible roles a user can have in a project.
|
-- 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 ('project_manager');
|
||||||
INSERT OR IGNORE INTO project_role (p_role) VALUES ('member');
|
INSERT OR IGNORE INTO project_role (p_role) VALUES ('member');
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
CREATE TABLE IF NOT EXISTS user_roles (
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
project_id INTEGER NOT NULL,
|
project_id INTEGER NOT NULL,
|
||||||
p_role TEXT NOT NULL, -- 'admin' or 'member'
|
p_role TEXT NOT NULL, -- 'project_manager' or 'member'
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
FOREIGN KEY (project_id) REFERENCES projects (id)
|
FOREIGN KEY (project_id) REFERENCES projects (id)
|
||||||
FOREIGN KEY (p_role) REFERENCES project_role (p_role)
|
FOREIGN KEY (p_role) REFERENCES project_role (p_role)
|
||||||
|
|
|
@ -11,12 +11,12 @@ import (
|
||||||
|
|
||||||
// The actual interface that we will use
|
// The actual interface that we will use
|
||||||
type GlobalState interface {
|
type GlobalState interface {
|
||||||
Register(c *fiber.Ctx) error // To register a new user
|
Register(c *fiber.Ctx) error // To register a new user
|
||||||
UserDelete(c *fiber.Ctx) error // To delete a user
|
UserDelete(c *fiber.Ctx) error // To delete a user
|
||||||
Login(c *fiber.Ctx) error // To get the token
|
Login(c *fiber.Ctx) error // To get the token
|
||||||
LoginRenew(c *fiber.Ctx) error // To renew the token
|
LoginRenew(c *fiber.Ctx) error // To renew the token
|
||||||
CreateProject(c *fiber.Ctx) error // To create a new project
|
CreateProject(c *fiber.Ctx) error // To create a new project
|
||||||
// GetProjects(c *fiber.Ctx) error // To get all projects
|
GetUserProjects(c *fiber.Ctx) error // To get all projects
|
||||||
// GetProject(c *fiber.Ctx) error // To get a specific project
|
// GetProject(c *fiber.Ctx) error // To get a specific project
|
||||||
// UpdateProject(c *fiber.Ctx) error // To update a project
|
// UpdateProject(c *fiber.Ctx) error // To update a project
|
||||||
// DeleteProject(c *fiber.Ctx) error // To delete a project
|
// DeleteProject(c *fiber.Ctx) error // To delete a project
|
||||||
|
@ -33,6 +33,9 @@ type GlobalState interface {
|
||||||
// SignCollection(c *fiber.Ctx) error // To sign a collection
|
// SignCollection(c *fiber.Ctx) error // To sign a collection
|
||||||
GetButtonCount(c *fiber.Ctx) error // For demonstration purposes
|
GetButtonCount(c *fiber.Ctx) error // For demonstration purposes
|
||||||
IncrementButtonCount(c *fiber.Ctx) error // For demonstration purposes
|
IncrementButtonCount(c *fiber.Ctx) error // For demonstration purposes
|
||||||
|
ListAllUsers(c *fiber.Ctx) error // To get a list of all users in the application database
|
||||||
|
ListAllUsersProject(c *fiber.Ctx) error // To get a list of all users for a specific project
|
||||||
|
ProjectRoleChange(c *fiber.Ctx) error // To change a users role in a project
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Constructor"
|
// "Constructor"
|
||||||
|
@ -163,3 +166,62 @@ func (gs *GState) CreateProject(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return c.Status(200).SendString("Project added")
|
return c.Status(200).SendString("Project added")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserProjects returns all projects that the user is a member of
|
||||||
|
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
|
||||||
|
// First we get the username from the token
|
||||||
|
user := c.Locals("user").(*jwt.Token)
|
||||||
|
claims := user.Claims.(jwt.MapClaims)
|
||||||
|
username := claims["name"].(string)
|
||||||
|
|
||||||
|
// Then dip into the database to get the projects
|
||||||
|
projects, err := gs.Db.GetProjectsForUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a json serialized list of projects
|
||||||
|
return c.JSON(projects)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAllUsers is a handler that returns a list of all users in the application database
|
||||||
|
func (gs *GState) ListAllUsers(c *fiber.Ctx) error {
|
||||||
|
// Get all users from the database
|
||||||
|
users, err := gs.Db.GetAllUsersApplication()
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the list of users as JSON
|
||||||
|
return c.JSON(users)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error {
|
||||||
|
// Extract the project name from the request parameters or body
|
||||||
|
projectName := c.Params("projectName")
|
||||||
|
|
||||||
|
// Get all users associated with the project from the database
|
||||||
|
users, err := gs.Db.GetAllUsersProject(projectName)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the list of users as JSON
|
||||||
|
return c.JSON(users)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectRoleChange is a handler that changes a user's role within a project
|
||||||
|
func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error {
|
||||||
|
// Extract the necessary parameters from the request
|
||||||
|
username := c.Params("username")
|
||||||
|
projectName := c.Params("projectName")
|
||||||
|
role := c.Params("role")
|
||||||
|
|
||||||
|
// Change the user's role within the project in the database
|
||||||
|
if err := gs.Db.ChangeUserRole(username, projectName, role); err != nil {
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a success message
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Project is a struct that holds the information about a project
|
// Project is a struct that holds the information about a project
|
||||||
type Project struct {
|
type Project struct {
|
||||||
ID int `json:"id" db:"id"`
|
ID int `json:"id" db:"id"`
|
||||||
Name string `json:"name" db:"name"`
|
Name string `json:"name" db:"name"`
|
||||||
Description string `json:"description" db:"description"`
|
Description string `json:"description" db:"description"`
|
||||||
Owner string `json:"owner" db:"owner"`
|
Owner string `json:"owner" db:"owner_user_id"`
|
||||||
Created time.Time `json:"created" db:"created"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// As it arrives from the client
|
// As it arrives from the client
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/swagger"
|
"github.com/gofiber/swagger"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
|
|
||||||
jwtware "github.com/gofiber/contrib/jwt"
|
jwtware "github.com/gofiber/contrib/jwt"
|
||||||
)
|
)
|
||||||
|
@ -69,8 +68,10 @@ func main() {
|
||||||
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
server.Get("/api/getUserProjects", gs.GetUserProjects)
|
||||||
server.Post("/api/loginrenew", gs.LoginRenew)
|
server.Post("/api/loginrenew", gs.LoginRenew)
|
||||||
server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches
|
server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches
|
||||||
|
server.Post("/api/project", gs.CreateProject)
|
||||||
|
|
||||||
// Announce the port we are listening on and start the server
|
// Announce the port we are listening on and start the server
|
||||||
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { NewProject, Project } from "../Types/Project";
|
||||||
import { NewUser, User } from "../Types/Users";
|
import { NewUser, User } from "../Types/Users";
|
||||||
|
|
||||||
// Defines all the methods that an instance of the API must implement
|
// Defines all the methods that an instance of the API must implement
|
||||||
|
@ -6,6 +7,10 @@ interface API {
|
||||||
registerUser(user: NewUser): Promise<User>;
|
registerUser(user: NewUser): Promise<User>;
|
||||||
/** Remove a user */
|
/** Remove a user */
|
||||||
removeUser(username: string): Promise<User>;
|
removeUser(username: string): Promise<User>;
|
||||||
|
/** Create a project */
|
||||||
|
createProject(project: NewProject): Promise<Project>;
|
||||||
|
/** Renew the token */
|
||||||
|
renewToken(token: string): Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export an instance of the API
|
// Export an instance of the API
|
||||||
|
@ -29,4 +34,24 @@ export const api: API = {
|
||||||
body: JSON.stringify(username),
|
body: JSON.stringify(username),
|
||||||
}).then((res) => res.json() as Promise<User>);
|
}).then((res) => res.json() as Promise<User>);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async createProject(project: NewProject): Promise<Project> {
|
||||||
|
return fetch("/api/project", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(project),
|
||||||
|
}).then((res) => res.json() as Promise<Project>);
|
||||||
|
},
|
||||||
|
|
||||||
|
async renewToken(token: string): Promise<string> {
|
||||||
|
return fetch("/api/loginrenew", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: "Bearer " + token,
|
||||||
|
},
|
||||||
|
}).then((res) => res.json() as Promise<string>);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
74
frontend/src/Components/Register.tsx
Normal file
74
frontend/src/Components/Register.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { NewUser } from "../Types/Users";
|
||||||
|
import { api } from "../API/API";
|
||||||
|
|
||||||
|
export default function Register(): JSX.Element {
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
|
||||||
|
const handleRegister = async (): Promise<void> => {
|
||||||
|
const newUser: NewUser = { userName: username, password };
|
||||||
|
await api.registerUser(newUser); // TODO: Handle errors
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="w-full max-w-xs">
|
||||||
|
<form
|
||||||
|
className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
void handleRegister();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3 className="pb-2">Register new user</h3>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label
|
||||||
|
className="block text-gray-700 text-sm font-bold mb-2"
|
||||||
|
htmlFor="username"
|
||||||
|
>
|
||||||
|
Username
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||||
|
id="username"
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => {
|
||||||
|
setUsername(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-6">
|
||||||
|
<label
|
||||||
|
className="block text-gray-700 text-sm font-bold mb-2"
|
||||||
|
htmlFor="password"
|
||||||
|
>
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
placeholder="Choose your password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => {
|
||||||
|
setPassword(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<button
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p className="text-center text-gray-500 text-xs"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -29,15 +29,15 @@ const PreloadBackgroundAnimation = (): JSX.Element => {
|
||||||
function LoginPage(): JSX.Element {
|
function LoginPage(): JSX.Element {
|
||||||
//Example users for testing without backend, remove when using backend
|
//Example users for testing without backend, remove when using backend
|
||||||
const admin: NewUser = {
|
const admin: NewUser = {
|
||||||
name: "admin",
|
userName: "admin",
|
||||||
password: "123",
|
password: "123",
|
||||||
};
|
};
|
||||||
const pmanager: NewUser = {
|
const pmanager: NewUser = {
|
||||||
name: "pmanager",
|
userName: "pmanager",
|
||||||
password: "123",
|
password: "123",
|
||||||
};
|
};
|
||||||
const user: NewUser = {
|
const user: NewUser = {
|
||||||
name: "user",
|
userName: "user",
|
||||||
password: "123",
|
password: "123",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,11 +48,14 @@ function LoginPage(): JSX.Element {
|
||||||
function handleSubmit(event: FormEvent<HTMLFormElement>): void {
|
function handleSubmit(event: FormEvent<HTMLFormElement>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
//TODO: Compare with db instead when finished
|
//TODO: Compare with db instead when finished
|
||||||
if (username === admin.name && password === admin.password) {
|
if (username === admin.userName && password === admin.password) {
|
||||||
navigate("/admin-menu");
|
navigate("/admin-menu");
|
||||||
} else if (username === pmanager.name && password === pmanager.password) {
|
} else if (
|
||||||
|
username === pmanager.userName &&
|
||||||
|
password === pmanager.password
|
||||||
|
) {
|
||||||
navigate("/PM-project-page");
|
navigate("/PM-project-page");
|
||||||
} else if (username === user.name && password === user.password) {
|
} else if (username === user.userName && password === user.password) {
|
||||||
navigate("/your-projects");
|
navigate("/your-projects");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
frontend/src/Types/Project.ts
Normal file
13
frontend/src/Types/Project.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export interface Project {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
owner: string;
|
||||||
|
created: string; // This is a date
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NewProject {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
owner: string;
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
// This is how the API responds
|
// This is how the API responds
|
||||||
export interface User {
|
export interface User {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
userName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to create a new user
|
// Used to create a new user
|
||||||
export interface NewUser {
|
export interface NewUser {
|
||||||
name: string;
|
userName: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
import LoginPage from "./Pages/LoginPage.tsx";
|
import LoginPage from "./Pages/LoginPage.tsx";
|
||||||
import YourProjectsPage from "./Pages/YourProjectsPage.tsx";
|
import YourProjectsPage from "./Pages/YourProjectsPage.tsx";
|
||||||
import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx";
|
import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx";
|
||||||
|
import Register from "./Components/Register.tsx";
|
||||||
import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx";
|
import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx";
|
||||||
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx";
|
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx";
|
||||||
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx";
|
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx";
|
||||||
|
@ -52,6 +53,14 @@ const router = createBrowserRouter([
|
||||||
path: "/project",
|
path: "/project",
|
||||||
element: <UserProjectPage />,
|
element: <UserProjectPage />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/register",
|
||||||
|
element: <Register />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/admin-menu",
|
||||||
|
element: <AdminMenuPage />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/project-page",
|
path: "/project-page",
|
||||||
element: <UserViewTimeReportsPage />,
|
element: <UserViewTimeReportsPage />,
|
||||||
|
|
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "TTime",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
Loading…
Reference in a new issue