Skip to content

Commit

Permalink
zess: add a frontend
Browse files Browse the repository at this point in the history
* vinvoor: init

* zess: development docker

* vingo: customizable redirect

* zess: add -c option

* vinvoor: add login

* vingo: add cors

* vingo: add json serialization

* vinvoor: add login and logout

* vinvoor: add a cards page

* zess: add development instructions

* vinvoor: add a theme

* vinvoor: refactor

* vinvoor: add support for dark and light mode

* vinvoor: move url's to .env

* zess: start vinvoor as non root user

* vinvoor: add a cards overview

* vingo: more serialization

* vinvoor: refactor the cards page

* vinvoor: change to camelCase

* vinvoor: add a leaderboard

* vinvoor: add a welcome page

* zess: change env var to export in dev script

* vingo: add database migrations

* vingo: fix api returning null when no results

* vingo: add card register via api

* vingo: add card id and name

* vinvoor: add a github activity style overview

* vinvoor: support adding new cards

* vinvoor: show current checkin status

* vinvoor: show current streak days

* vinvoor: show the user's most common days

* vinvoor: simple overview page

* vinvoor: fix crash when no scans are registered

* vinvoor: center graphs in the overview

* vinvoor: add support for new comers

* vingo: move to gorm

* vingo: go get -u && go mod tidy

* vingo: card register status endpoint

* vingo: ability to add name to card

* vingo: return if last register was success

* vingo: return extra stats for cards

* vingo: time remaining on card status endpoint

* vinvoor: support new card features

* vingo: add leaderboard position change

* vingo: pieter post pieter post pieter post verdient de kost (met zijn post)

* vinvoor: pieter post deed ambetant

* vinvoor: show position changes in the leaderboard

* vinvoor: show a scan overview

* vingo: fix scans

* vingo: card handlers to receivers

* vingo: settings endpoint

* vingo: cleanup

* vinvoor: add a new zess logo

---------

Co-authored-by: Topvennie <[email protected]>
  • Loading branch information
hannes-dev and Topvennie authored Jul 20, 2024
1 parent 2edb53a commit c697694
Show file tree
Hide file tree
Showing 72 changed files with 2,750 additions and 1,447 deletions.
48 changes: 20 additions & 28 deletions vingo/database/cards.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,35 @@
package database

import "time"

type Card struct {
Serial string `json:"serial"`
CreatedAt time.Time `json:"createdAt"`
}

var (
cardsCreateStmt = `
CREATE TABLE IF NOT EXISTS cards (
serial TEXT NOT NULL PRIMARY KEY UNIQUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id)
);
`
)

func CreateCard(serial string, user_id int) error {
_, err := db.Exec("INSERT INTO cards (serial, user_id) VALUES ($1, $2);", serial, user_id)
return err
return gorm_db.Create(&Card{Serial: serial, UserId: user_id}).Error
}

func GetCardsForUser(user_id int) ([]Card, error) {
rows, err := db.Query("SELECT serial, created_at FROM cards WHERE user_id = $1;", user_id)
var cards []Card
result := gorm_db.Where("user_id = ?", user_id).Find(&cards)
return cards, result.Error
}

func GetCardsAndStatsForUser(user_id int) ([]CardAPI, error) {
rows, err := db.Query(`
SELECT cards.id, cards.created_at, serial, name, COUNT(scans.id), (select MAX(scan_time) from scans where card_serial = cards.serial) from cards LEFT JOIN scans on scans.card_serial = serial WHERE
user_id = $1 GROUP BY cards.id;
`, user_id)

if err != nil {
return nil, err
}
defer rows.Close()

cards := make([]Card, 0)
cards := []CardAPI{}
for rows.Next() {
var card Card
err := rows.Scan(&card.Serial, &card.CreatedAt)
if err != nil {
return nil, err
}
cards = append(cards, card)
var item CardAPI
_ = rows.Scan(&item.Id, &item.CreatedAt, &item.Serial, &item.Name, &item.AmountUsed, &item.LastUsed)
cards = append(cards, item)
}

return cards, nil
}

func UpdateCardName(id int, name string, user_id int) error {
return gorm_db.Model(&Card{}).Where("id = ? AND user_id = ?", id, user_id).Update("name", name).Error
}
9 changes: 0 additions & 9 deletions vingo/database/days.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ type Day struct {
Date time.Time
}

var (
daysCreateStmt = `
CREATE TABLE IF NOT EXISTS days (
id SERIAL NOT NULL PRIMARY KEY,
date DATE NOT NULL UNIQUE
);
`
)

func CreateDays(first_day time.Time, last_day time.Time) error {
tx, err := db.Begin()
if err != nil {
Expand Down
41 changes: 23 additions & 18 deletions vingo/database/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,39 @@ package database
import (
"database/sql"
"log"

"gorm.io/driver/postgres"
"gorm.io/gorm"
)

var (
db *sql.DB
db *sql.DB
gorm_db *gorm.DB
)

func createTables() {
// Tables to create
createStmts := []string{usersCreateStmt, settingsCreateStmt, cardsCreateStmt, scansCreateStmt, daysCreateStmt}
for _, stmt := range createStmts {
_, err := db.Exec(stmt)
if err != nil {
log.Println("Error creating table with query: \n", stmt)
log.Fatal(err)
}
}
}

func Get() *sql.DB {
return db
}

func OpenDatabase(conn string) {
new_db, err := sql.Open("postgres", conn)
func OpenDatabase(db_string string) {
new_db, err := gorm.Open(postgres.Open(db_string), &gorm.Config{})
if err != nil {
log.Panicln("Error opening database connection")
log.Println("Error opening database connection")
log.Fatal(err)
}
db = new_db
createTables()

err = new_db.AutoMigrate()
if err != nil {
log.Println("Error migrating database")
log.Fatal(err)
}

err = new_db.AutoMigrate(&User{}, &Card{}, &Scan{}, &Day{}, &Settings{}, &Season{})
if err != nil {
log.Println("Error migrating database")
log.Fatal(err)
}

gorm_db = new_db
db, _ = new_db.DB()
}
70 changes: 70 additions & 0 deletions vingo/database/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package database

import (
"time"

"gorm.io/gorm"
)

type BaseModel struct {
Id int `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}

type User struct {
BaseModel
Username string `json:"username"`
Admin bool `json:"admin"`
SettingsId int
Settings Settings `json:"settings"`
Cards []Card `json:"-" gorm:"foreignKey:UserId;references:Id"`
}

type Settings struct {
BaseModel
ScanInOut bool `json:"scanInOut"`
Leaderboard bool `json:"leaderboard"`
Public bool `json:"public"`
}

type Card struct {
BaseModel
Serial string `gorm:"uniqueIndex"`
Name string
UserId int
User User
Scans []Scan `gorm:"foreignKey:CardSerial;references:Serial"`
}

func Card_to_API(card Card) CardAPI {
var lastUsed time.Time = card.CreatedAt
if len(card.Scans) != 0 {
lastUsed = card.Scans[len(card.Scans)-1].ScanTime
}

return CardAPI{
Id: card.Id,
Serial: card.Serial,
Name: card.Name,
LastUsed: lastUsed,
AmountUsed: len(card.Scans),
}
}

type CardAPI struct {
Id int `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Serial string `json:"serial"`
Name string `json:"name"`
LastUsed time.Time `json:"lastUsed"`
AmountUsed int `json:"amountUsed"`
}

type Scan struct {
BaseModel
ScanTime time.Time `json:"scanTime"`
CardSerial string `json:"cardSerial" gorm:"index"`
Card Card `json:"-" gorm:"foreignKey:CardSerial;references:Serial"`
}
65 changes: 24 additions & 41 deletions vingo/database/scans.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package database

import "time"

type Scan struct {
ScanTime time.Time `json:"scanTime"`
Card string `json:"card"`
}
import (
"time"
)

type Present struct {
Date time.Time
Expand All @@ -14,42 +11,27 @@ type Present struct {
}

type LeaderboardItem struct {
Position int `json:"position"`
Username string `json:"username"`
TotalDays int `json:"totalDays"`
Position int `json:"position"`
UserId int `json:"userId"`
Username string `json:"username"`
TotalDays int `json:"totalDays"`
PositionChange int `json:"positionChange"`
}

var (
scansCreateStmt = `
CREATE TABLE IF NOT EXISTS scans (
id SERIAL NOT NULL PRIMARY KEY,
scan_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
scan_in BOOLEAN,
card_serial TEXT NOT NULL REFERENCES cards(serial)
);
`
)

func CreateScan(card_serial string) error {
_, err := db.Exec("INSERT INTO scans (card_serial) VALUES ($1);", card_serial)
return err
return gorm_db.Create(&Scan{ScanTime: time.Now(), CardSerial: card_serial}).Error
}

func GetScansForUser(user_id int) ([]Scan, error) {
scans_rows, err := db.Query("SELECT scan_time, card_serial FROM scans WHERE card_serial IN (SELECT serial FROM cards WHERE user_id = $1) ORDER BY scan_time DESC;", user_id)
if err != nil {
return nil, err
}
var user User
result := gorm_db.Preload("Cards.Scans").First(&user, user_id)

var scans []Scan

for scans_rows.Next() {
var scan Scan
_ = scans_rows.Scan(&scan.ScanTime, &scan.Card)

scans = append(scans, scan)
for _, card := range user.Cards {
scans = append(scans, card.Scans...)
}
return scans, nil

return scans, result.Error
}

func GetPresenceHistory(user_id int) ([]Present, error) {
Expand Down Expand Up @@ -78,7 +60,7 @@ func GetPresenceHistory(user_id int) ([]Present, error) {
return nil, err
}

var presences []Present
presences := []Present{}
for rows.Next() {
var present Present
_ = rows.Scan(&present.Date, &present.Present, &present.StreakDay)
Expand All @@ -89,24 +71,25 @@ func GetPresenceHistory(user_id int) ([]Present, error) {
return presences, nil
}

func TotalDaysPerUser() ([]LeaderboardItem, error) {
func TotalDaysPerUser(before_time time.Time) ([]LeaderboardItem, error) {
rows, err := db.Query(`
SELECT count, username, RANK() OVER (ORDER BY count desc) AS position
FROM (SELECT COUNT(DISTINCT ((scan_time - INTERVAL '4 hours') AT TIME ZONE 'Europe/Brussels')::date), username
SELECT user_id, count, username, RANK() OVER (ORDER BY count desc) AS position
FROM (SELECT COUNT(DISTINCT ((scan_time - INTERVAL '4 hours') AT TIME ZONE 'Europe/Brussels')::date), username, users.id as user_id
FROM scans
LEFT JOIN cards ON card_serial = serial
LEFT JOIN users ON user_id = users.id
GROUP BY username);
`)
WHERE scan_time < $1
GROUP BY username, users.id);
`, before_time)

if err != nil {
return nil, err
}

var leaderboard []LeaderboardItem
leaderboard := []LeaderboardItem{}
for rows.Next() {
var item LeaderboardItem
_ = rows.Scan(&item.TotalDays, &item.Username, &item.Position)
_ = rows.Scan(&item.UserId, &item.TotalDays, &item.Username, &item.Position)

leaderboard = append(leaderboard, item)
}
Expand Down
24 changes: 3 additions & 21 deletions vingo/database/settings.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
package database

type Settings struct {
ScanInOut bool `json:"scanInOut"`
Leaderboard bool `json:"leaderboard"`
Public bool `json:"public"`
}

var (
settingsCreateStmt = `
CREATE TABLE IF NOT EXISTS settings (
user_id INT NOT NULL PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
public BOOLEAN NOT NULL DEFAULT FALSE,
scan_in_out BOOLEAN NOT NULL DEFAULT FALSE,
leaderboard BOOLEAN NOT NULL DEFAULT TRUE
);
`
)

func CreateSettings(user_id int) error {
_, err := db.Exec("INSERT INTO settings (user_id) VALUES ($1) ON CONFLICT DO NOTHING;", user_id)
return err
}

func GetSettings(user_id int) (*Settings, error) {
row := db.QueryRow("SELECT scan_in_out, leaderboard, public FROM settings WHERE user_id = $1;", user_id)
settings := new(Settings)
err := row.Scan(&settings.ScanInOut, &settings.Leaderboard, &settings.Public)
return settings, err
var settings Settings
result := gorm_db.First(&settings, "user_id = ?", user_id)
return &settings, result.Error
}

func UpdateSettings(user_id int, settings Settings) error {
Expand Down
Loading

0 comments on commit c697694

Please sign in to comment.