Compare commits

..

43 commits

Author SHA1 Message Date
Imbus
4d23f8acea Merge branch 'frontend' of github.com:imbus64/TTime into frontend 2024-03-13 13:51:35 +01:00
Imbus
8c8e57cb50 Merge branch 'frontend' 2024-03-13 13:42:49 +01:00
Imbus
25fdf3bb9b Nuke dead code 2024-03-13 12:29:15 +01:00
Imbus
c4632104a8 Commenting out trigger related to salts 2024-03-13 11:46:17 +01:00
Imbus
736cebe036 Sql comments and salts table 2024-03-12 20:44:54 +01:00
Imbus
9b67a580da Gluecode for database/handlers 2024-03-12 20:44:40 +01:00
borean
ad4d439887 cleanup 2024-03-08 15:07:25 +01:00
borean
d06122864e pls no error 2024-03-08 15:06:32 +01:00
borean
f8277617a9 Merge remote-tracking branch 'refs/remotes/origin/dev' into dev 2024-03-08 15:02:18 +01:00
borean
6acfdd36b2 cleanup 2024-03-08 14:56:24 +01:00
borean
0b23c5f8f1 Merge branch 'borean-dev' into dev 2024-03-08 14:55:10 +01:00
Mattias
ac6638b344 Removed example pages Home and Settings aswell as the react-logo 2024-03-08 11:36:36 +01:00
Imbus
394b283712 Typo 2024-03-08 11:25:31 +01:00
Imbus
02c9c502b8 Eralchemy2 target for generating er diagrams 2024-03-08 11:22:09 +01:00
Imbus
a7ebfe7110 Ignore formatter output in main 2024-03-08 10:32:02 +01:00
Imbus
efc95e7fea Add target for formatting swag docs & formatting swag docs 2024-03-08 10:30:17 +01:00
Imbus
b52f13ee8b Better developer feedback 2024-03-08 10:25:22 +01:00
Imbus
d4a2a00f69 Makefile update with doc generation and convenience installers 2024-03-08 10:17:51 +01:00
Imbus
ae0b0895b2 Version controlling generated docs 2024-03-08 10:17:27 +01:00
Imbus
13c12b424a Mounting handlers and basic swagger docs example 2024-03-08 10:16:56 +01:00
Imbus
f75cb7e9fc Pulling in deps related to swagger 2024-03-08 10:15:05 +01:00
Imbus
bb81a2973e Moving main out of cmd to accomodate swag cli tool 2024-03-08 09:10:20 +01:00
Imbus
944f9b6ccc Removed overly verbose debug printing from db migration 2024-03-08 07:48:25 +01:00
Imbus
12536d5e89 Fixing inconsistent sql in db interface 2024-03-08 07:47:32 +01:00
Imbus
2351a0cb4a Github actions CI for db migrations 2024-03-08 07:42:11 +01:00
Imbus
a77b388f8e Warning in make migrate target 2024-03-08 07:34:26 +01:00
Imbus
f5d5eee267 Breaking changes in projects table related to naming 2024-03-08 07:33:58 +01:00
Imbus
553ba2c7c6 New project_role table to represent possible roles 2024-03-08 07:33:31 +01:00
borean
eec3a45509 Added GetProjectId and AddUserToProject, WIP 2024-03-07 23:24:54 +01:00
borean
5a85f2bf81 first draft of AddTimeReport in db.go 2024-03-07 21:57:27 +01:00
borean
7ed986e4eb update 2024-03-07 20:58:50 +01:00
Peter KW
d7cf291836 added new page for admin and path in main 2024-03-07 17:48:43 +01:00
dDogge
1c5cbd768d Merge remote-tracking branch 'refs/remotes/gh/dev' into dev 2024-03-07 14:25:55 +01:00
dDogge
bd434c3da7 Added template for various new functions in db.go and corresponding tests in db_test.go 2024-03-07 14:25:28 +01:00
Imbus
c9406b8e50 Ported Justfile to Makefile 2024-03-07 14:16:50 +01:00
Imbus
9b9f6dd962 Merge branch 'dev' of git.silversoft.se:Imbus/TTime into dev 2024-03-07 13:30:26 +01:00
dDogge
18eefab292 Implemented PromoteToAdmin and corresponding Test 2024-03-07 13:21:47 +01:00
dDogge
3adf0b7ef5 Admin SQL table added 2024-03-07 13:09:09 +01:00
Imbus
a202eaa610 Readme wsl notes 2024-03-07 12:26:08 +01:00
Imbus
acff254aa5 Indirect -> direct dependency in go.mod 2024-03-07 12:13:29 +01:00
Imbus
3175c62e6d Carmack level testing instructions 2024-03-07 11:33:06 +01:00
borean
ae9ee91bc4 file correction 2024-03-04 19:34:35 +01:00
borean
a22dcb9f4e first push, broken import, WIP 2024-03-04 02:50:02 +01:00
26 changed files with 512 additions and 85 deletions

View file

@ -0,0 +1,25 @@
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

1
.gitignore vendored
View file

@ -7,6 +7,7 @@
bin
db.sqlite3
*.png
# Test binary, built with `go test -c`
*.test

41
Makefile Normal file
View file

@ -0,0 +1,41 @@
# 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

View file

@ -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
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.

View file

@ -13,7 +13,7 @@ MIGRATIONS_DIR = internal/database/migrations
# Build target
build:
$(GOBUILD) -o bin/server cmd/*.go
$(GOBUILD) -o bin/server main.go
# Run target
run: build
@ -43,6 +43,7 @@ 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"; \
@ -70,6 +71,31 @@ 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

80
backend/docs/docs.go Normal file
View file

@ -0,0 +1,80 @@
// 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)
}

View file

@ -8,10 +8,36 @@ 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 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/gofiber/contrib/jwt v1.0.8
github.com/golang-jwt/jwt/v5 v5.2.1
)
// These are all for fiber

View file

@ -1,11 +1,28 @@
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=
@ -14,20 +31,28 @@ 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=
@ -38,10 +63,21 @@ 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=
@ -50,9 +86,24 @@ 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=

View file

@ -2,9 +2,9 @@ package database
import (
"embed"
"log"
"os"
"path/filepath"
"time"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
@ -12,11 +12,20 @@ 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
@ -29,7 +38,11 @@ type Db struct {
var scripts embed.FS
const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)"
const projectInsert = "INSERT INTO projects (name, description, 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 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 = ""
// DbConnect connects to the database
func DbConnect(dbpath string) Database {
@ -48,6 +61,32 @@ 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)
@ -60,9 +99,20 @@ 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)
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)
return id, err
}
@ -101,7 +151,6 @@ func (d *Db) Migrate(dirname string) error {
if err != nil {
return err
}
log.Println("Executed SQL file:", file.Name())
}
if tr.Commit() != nil {

View file

@ -74,3 +74,32 @@ 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) {
// }

View file

@ -1,3 +1,7 @@
-- 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,
@ -5,5 +9,6 @@ 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);

View file

@ -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,
user_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
owner_user_id INTEGER NOT NULL,
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 (user_id);
CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (owner_user_id);

View file

@ -0,0 +1,9 @@
-- 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');

View file

@ -1,16 +1,9 @@
CREATE TABLE IF NOT EXISTS user_roles (
user_id INTEGER NOT NULL,
project_id INTEGER NOT NULL,
role STRING NOT NULL, -- 'admin' or 'member'
p_role TEXT 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)
);
-- 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;
);

View file

@ -0,0 +1,4 @@
CREATE TABLE IF NOT EXISTS site_admin (
admin_id INTEGER PRIMARY KEY,
FOREIGN KEY (admin_id) REFERENCES users (id) ON DELETE CASCADE
)

View file

@ -0,0 +1,16 @@
-- 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;

View file

@ -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,8 +46,19 @@ 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.User)
u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
return c.Status(400).SendString(err.Error())
}
@ -115,6 +126,7 @@ 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 <token>"
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
@ -130,3 +142,24 @@ 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")
}

View file

@ -0,0 +1,21 @@
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"`
}

View file

@ -16,6 +16,11 @@ 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"`

View file

@ -1,18 +1,34 @@
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 {
@ -20,9 +36,11 @@ func main() {
_ = conf.WriteConfigToFile("config.toml")
}
// Pretty print the current config
str, _ := json.MarshalIndent(conf, "", " ")
fmt.Println(string(str))
// 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)
// Connect to the database
db := database.DbConnect(conf.DbPath)
@ -31,6 +49,8 @@ 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")

View file

@ -0,0 +1,27 @@
import { Link } from "react-router-dom";
import BasicWindow from "../../Components/BasicWindow";
function AdminMenuPage(): JSX.Element {
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">Administrator Menu</h1>
<div className="border-4 border-black bg-white flex flex-col items-center justify-center min-h-[65vh] h-fit w-[50vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px]">
<Link to="/admin-users-page">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Users
</h1>
</Link>
<Link to="/admin-projects-page">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Projects
</h1>
</Link>
</div>
</>
);
const buttons = <></>;
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminMenuPage;

View file

@ -1,36 +0,0 @@
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 (
<>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} className="logo h-32 p-5" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank" rel="noreferrer">
<img
src={reactLogo}
className="logo react h-32 p-5"
alt="React logo"
/>
</a>
</div>
<h1>Vite + React</h1>
<div className="card flex flex-col items-center space-y-4">
<CountButton />
<Link to="/settings">To Settings</Link>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}

View file

@ -1,17 +0,0 @@
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 (
<>
<h1>Very Fancy Settings Page</h1>
<div className="card">
<Link to="/">To Home</Link>
</div>
</>
);
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4 KiB

View file

@ -5,6 +5,7 @@ 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([
@ -20,6 +21,10 @@ const router = createBrowserRouter([
path: "/project",
element: <UserProjectPage />,
},
{
path: "/admin-menu",
element: <AdminMenuPage />,
},
]);
// Semi-hacky way to get the root element

15
go.work.sum Normal file
View file

@ -0,0 +1,15 @@
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=