Display user storage usage on the dashboard
This commit is contained in:
102
db/database.go
102
db/database.go
@ -5,12 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fossyy/filekeeper/types"
|
"github.com/fossyy/filekeeper/types"
|
||||||
"github.com/fossyy/filekeeper/types/models"
|
"github.com/fossyy/filekeeper/types/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
gormLogger "gorm.io/gorm/logger"
|
gormLogger "gorm.io/gorm/logger"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type mySQLdb struct {
|
type mySQLdb struct {
|
||||||
@ -71,21 +70,20 @@ func NewMYSQLdb(username, password, host, port, dbName string) types.Database {
|
|||||||
panic("failed to connect database: " + err.Error())
|
panic("failed to connect database: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile("schema.sql")
|
err = DB.AutoMigrate(&models.User{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Error opening file: " + err.Error())
|
panic(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
err = DB.AutoMigrate(&models.File{})
|
||||||
queries := strings.Split(string(file), ";")
|
|
||||||
for _, query := range queries {
|
|
||||||
query = strings.TrimSpace(query)
|
|
||||||
if query == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err := DB.Exec(query).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Error executing query: " + err.Error())
|
panic(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
err = DB.AutoMigrate(&models.Allowance{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return &mySQLdb{DB}
|
return &mySQLdb{DB}
|
||||||
}
|
}
|
||||||
@ -101,6 +99,10 @@ func NewPostgresDB(username, password, host, port, dbName string, mode SSLMode)
|
|||||||
Logger: gormLogger.Default.LogMode(gormLogger.Silent),
|
Logger: gormLogger.Default.LogMode(gormLogger.Silent),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to connect database: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
initDB.Raw("SELECT count(*) FROM pg_database WHERE datname = ?", dbName).Scan(&count)
|
initDB.Raw("SELECT count(*) FROM pg_database WHERE datname = ?", dbName).Scan(&count)
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
if err := initDB.Exec("CREATE DATABASE " + dbName).Error; err != nil {
|
if err := initDB.Exec("CREATE DATABASE " + dbName).Error; err != nil {
|
||||||
@ -119,23 +121,21 @@ func NewPostgresDB(username, password, host, port, dbName string, mode SSLMode)
|
|||||||
panic("failed to connect database: " + err.Error())
|
panic("failed to connect database: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile("schema.sql")
|
err = DB.AutoMigrate(&models.User{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Error opening file: " + err.Error())
|
panic(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
err = DB.AutoMigrate(&models.File{})
|
||||||
queries := strings.Split(string(file), ";")
|
|
||||||
for _, query := range queries {
|
|
||||||
query = strings.TrimSpace(query)
|
|
||||||
if query == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err := DB.Exec(query).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Error executing query: " + err.Error())
|
panic(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
err = DB.AutoMigrate(&models.Allowance{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &postgresDB{DB}
|
return &postgresDB{DB}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +168,10 @@ func (db *mySQLdb) CreateUser(user *models.User) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = db.CreateAllowance(user.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +204,28 @@ func (db *mySQLdb) UpdateUserPassword(email string, password string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *mySQLdb) CreateAllowance(userID uuid.UUID) error {
|
||||||
|
userAllowance := &models.Allowance{
|
||||||
|
UserID: userID,
|
||||||
|
AllowanceByte: 1024 * 1024 * 1024 * 10,
|
||||||
|
AllowanceFile: 10,
|
||||||
|
}
|
||||||
|
err := db.DB.Create(userAllowance).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *mySQLdb) GetAllowance(userID uuid.UUID) (*models.Allowance, error) {
|
||||||
|
var allowance models.Allowance
|
||||||
|
err := db.DB.Table("allowances").Where("user_id = ?", userID).First(&allowance).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &allowance, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *mySQLdb) CreateFile(file *models.File) error {
|
func (db *mySQLdb) CreateFile(file *models.File) error {
|
||||||
err := db.DB.Create(file).Error
|
err := db.DB.Create(file).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -279,6 +305,10 @@ func (db *postgresDB) CreateUser(user *models.User) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = db.CreateAllowance(user.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +341,28 @@ func (db *postgresDB) UpdateUserPassword(email string, password string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *postgresDB) CreateAllowance(userID uuid.UUID) error {
|
||||||
|
userAllowance := &models.Allowance{
|
||||||
|
UserID: userID,
|
||||||
|
AllowanceByte: 1024 * 1024 * 1024 * 10,
|
||||||
|
AllowanceFile: 10,
|
||||||
|
}
|
||||||
|
err := db.DB.Create(userAllowance).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *postgresDB) GetAllowance(userID uuid.UUID) (*models.Allowance, error) {
|
||||||
|
var allowance models.Allowance
|
||||||
|
err := db.DB.Table("allowances").Where("user_id = $1", userID).First(&allowance).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &allowance, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *postgresDB) CreateFile(file *models.File) error {
|
func (db *postgresDB) CreateFile(file *models.File) error {
|
||||||
err := db.DB.Create(file).Error
|
err := db.DB.Create(file).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
6
go.mod
6
go.mod
@ -3,7 +3,7 @@ module github.com/fossyy/filekeeper
|
|||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/templ v0.2.707
|
github.com/a-h/templ v0.2.778
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
@ -36,8 +36,8 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.23.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
)
|
)
|
||||||
|
12
go.sum
12
go.sum
@ -1,7 +1,7 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM=
|
||||||
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
@ -70,14 +70,14 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
|
|||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -2,14 +2,14 @@ package userSessionTerminateHandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fossyy/filekeeper/session"
|
"github.com/fossyy/filekeeper/session"
|
||||||
|
"github.com/fossyy/filekeeper/types"
|
||||||
"github.com/fossyy/filekeeper/view/client/user"
|
"github.com/fossyy/filekeeper/view/client/user"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DELETE(w http.ResponseWriter, r *http.Request) {
|
func DELETE(w http.ResponseWriter, r *http.Request) {
|
||||||
id := r.PathValue("id")
|
id := r.PathValue("id")
|
||||||
|
mySession := r.Context().Value("user").(types.User)
|
||||||
_, mySession, _ := session.GetSession(r)
|
|
||||||
otherSession := session.Get(id)
|
otherSession := session.Get(id)
|
||||||
if _, err := session.GetSessionInfo(mySession.Email, otherSession.ID); err != nil {
|
if _, err := session.GetSessionInfo(mySession.Email, otherSession.ID); err != nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/fossyy/filekeeper/session"
|
"github.com/fossyy/filekeeper/session"
|
||||||
"github.com/fossyy/filekeeper/types"
|
"github.com/fossyy/filekeeper/types"
|
||||||
"github.com/fossyy/filekeeper/types/models"
|
"github.com/fossyy/filekeeper/types/models"
|
||||||
|
"github.com/fossyy/filekeeper/utils"
|
||||||
"github.com/fossyy/filekeeper/view/client/user"
|
"github.com/fossyy/filekeeper/view/client/user"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@ -57,24 +58,44 @@ func GET(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
handlerWS(upgrade, userSession)
|
handlerWS(upgrade, userSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
var component templ.Component
|
var component templ.Component
|
||||||
sessions, err := session.GetSessions(userSession.Email)
|
sessions, err := session.GetSessions(userSession.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowance, err := app.Server.Database.GetAllowance(userSession.UserID)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
usage, err := app.Server.Service.GetUserStorageUsage(userSession.UserID.String())
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
allowanceStats := &types.Allowance{
|
||||||
|
AllowanceByte: utils.ConvertFileSize(allowance.AllowanceByte),
|
||||||
|
AllowanceUsedByte: utils.ConvertFileSize(usage),
|
||||||
|
AllowanceUsedPercent: fmt.Sprintf("%.2f", float64(usage)/float64(allowance.AllowanceByte)*100),
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.URL.Query().Get("error"); err != "" {
|
if err := r.URL.Query().Get("error"); err != "" {
|
||||||
message, ok := errorMessages[err]
|
message, ok := errorMessages[err]
|
||||||
if !ok {
|
if !ok {
|
||||||
message = "Unknown error occurred. Please contact support at bagas@fossy.my.id for assistance."
|
message = "Unknown error occurred. Please contact support at bagas@fossy.my.id for assistance."
|
||||||
}
|
}
|
||||||
|
|
||||||
component = userView.Main("Filekeeper - User Page", userSession, sessions, types.Message{
|
component = userView.Main("Filekeeper - User Page", userSession, allowanceStats, sessions, types.Message{
|
||||||
Code: 0,
|
Code: 0,
|
||||||
Message: message,
|
Message: message,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
component = userView.Main("Filekeeper - User Page", userSession, sessions, types.Message{
|
component = userView.Main("Filekeeper - User Page", userSession, allowanceStats, sessions, types.Message{
|
||||||
Code: 1,
|
Code: 1,
|
||||||
Message: "",
|
Message: "",
|
||||||
})
|
})
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS users (user_id UUID PRIMARY KEY NOT NULL, username VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password TEXT NOT NULL, totp VARCHAR(255) NOT NULL);
|
|
||||||
CREATE TABLE IF NOT EXISTS files (id UUID PRIMARY KEY NOT NULL, owner_id UUID NOT NULL, name TEXT NOT NULL, size BIGINT NOT NULL, total_chunk BIGINT NOT NULL, downloaded BIGINT NOT NULL DEFAULT 0, FOREIGN KEY (owner_id) REFERENCES users(user_id));
|
|
@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"github.com/fossyy/filekeeper/app"
|
"github.com/fossyy/filekeeper/app"
|
||||||
"github.com/fossyy/filekeeper/types"
|
"github.com/fossyy/filekeeper/types"
|
||||||
"github.com/fossyy/filekeeper/types/models"
|
"github.com/fossyy/filekeeper/types/models"
|
||||||
@ -26,6 +27,7 @@ func (r *Service) GetUser(ctx context.Context, email string) (*models.User, erro
|
|||||||
userJSON, err := app.Server.Cache.GetCache(ctx, "UserCache:"+email)
|
userJSON, err := app.Server.Cache.GetCache(ctx, "UserCache:"+email)
|
||||||
if err == redis.Nil {
|
if err == redis.Nil {
|
||||||
userData, err := r.db.GetUser(email)
|
userData, err := r.db.GetUser(email)
|
||||||
|
fmt.Println(userData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -66,6 +68,18 @@ func (r *Service) DeleteUser(email string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Service) GetUserStorageUsage(ownerID string) (uint64, error) {
|
||||||
|
files, err := app.Server.Database.GetFiles(ownerID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var total uint64 = 0
|
||||||
|
for _, file := range files {
|
||||||
|
total += file.Size
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Service) GetFile(id string) (*models.File, error) {
|
func (r *Service) GetFile(id string) (*models.File, error) {
|
||||||
fileJSON, err := r.cache.GetCache(context.Background(), "FileCache:"+id)
|
fileJSON, err := r.cache.GetCache(context.Background(), "FileCache:"+id)
|
||||||
if err == redis.Nil {
|
if err == redis.Nil {
|
||||||
|
@ -3,18 +3,26 @@ package models
|
|||||||
import "github.com/google/uuid"
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
UserID uuid.UUID `gorm:"primaryKey;not null;unique"`
|
UserID uuid.UUID `gorm:"type:uuid;primaryKey"`
|
||||||
Username string `gorm:"unique;not null"`
|
Username string `gorm:"type:varchar(255);unique;not null"`
|
||||||
Email string `gorm:"unique;not null"`
|
Email string `gorm:"type:varchar(255);unique;not null"`
|
||||||
Password string `gorm:"not null"`
|
Password string `gorm:"type:text;not null"`
|
||||||
Totp string `gorm:"not null"`
|
Totp string `gorm:"type:varchar(255);not null"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
ID uuid.UUID `gorm:"primaryKey;not null;unique"`
|
ID uuid.UUID `gorm:"type:uuid;primaryKey"`
|
||||||
OwnerID uuid.UUID `gorm:"not null"`
|
OwnerID uuid.UUID `gorm:"type:uuid;not null"`
|
||||||
Name string `gorm:"not null"`
|
Name string `gorm:"type:text;not null"`
|
||||||
Size uint64 `gorm:"not null"`
|
Size uint64 `gorm:"not null"`
|
||||||
TotalChunk uint64 `gorm:"not null"`
|
TotalChunk uint64 `gorm:"not null"`
|
||||||
Downloaded uint64 `gorm:"not null;default=0"`
|
Downloaded uint64 `gorm:"not null;default:0"`
|
||||||
|
Owner *User `gorm:"foreignKey:OwnerID;constraint:OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Allowance struct {
|
||||||
|
UserID uuid.UUID `gorm:"type:uuid;primaryKey"`
|
||||||
|
AllowanceByte uint64 `gorm:"not null"`
|
||||||
|
AllowanceFile uint64 `gorm:"not null"`
|
||||||
|
User *User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE;"`
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@ type User struct {
|
|||||||
Authenticated bool
|
Authenticated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileInfo struct {
|
type Allowance struct {
|
||||||
Name string `json:"name"`
|
AllowanceByte string
|
||||||
Size uint64 `json:"size"`
|
AllowanceUsedByte string
|
||||||
Chunk uint64 `json:"chunk"`
|
AllowanceUsedPercent string
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileData struct {
|
type FileData struct {
|
||||||
@ -52,6 +52,9 @@ type Database interface {
|
|||||||
GetAllUsers() ([]models.User, error)
|
GetAllUsers() ([]models.User, error)
|
||||||
UpdateUserPassword(email string, password string) error
|
UpdateUserPassword(email string, password string) error
|
||||||
|
|
||||||
|
CreateAllowance(userID uuid.UUID) error
|
||||||
|
GetAllowance(userID uuid.UUID) (*models.Allowance, error)
|
||||||
|
|
||||||
CreateFile(file *models.File) error
|
CreateFile(file *models.File) error
|
||||||
GetFile(fileID string) (*models.File, error)
|
GetFile(fileID string) (*models.File, error)
|
||||||
GetUserFile(name string, ownerID string) (*models.File, error)
|
GetUserFile(name string, ownerID string) (*models.File, error)
|
||||||
@ -72,4 +75,5 @@ type Services interface {
|
|||||||
DeleteUser(email string)
|
DeleteUser(email string)
|
||||||
GetFile(id string) (*models.File, error)
|
GetFile(id string) (*models.File, error)
|
||||||
GetUserFile(name, ownerID string) (*FileWithDetail, error)
|
GetUserFile(name, ownerID string) (*FileWithDetail, error)
|
||||||
|
GetUserStorageUsage(ownerID string) (uint64, error)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/fossyy/filekeeper/session"
|
"github.com/fossyy/filekeeper/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
templ content(message types.Message, title string, user types.User, ListSession []*session.SessionInfo) {
|
templ content(message types.Message, title string, user types.User, allowance *types.Allowance, ListSession []*session.SessionInfo) {
|
||||||
@layout.BaseAuth(title){
|
@layout.BaseAuth(title){
|
||||||
@layout.Navbar(user)
|
@layout.Navbar(user)
|
||||||
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
||||||
@ -231,24 +231,19 @@ templ content(message types.Message, title string, user types.User, ListSession
|
|||||||
|
|
||||||
<div class="space-y-8">
|
<div class="space-y-8">
|
||||||
<div class="rounded-lg border bg-card text-card-foreground shadow-sm" data-v0-t="card">
|
<div class="rounded-lg border bg-card text-card-foreground shadow-sm" data-v0-t="card">
|
||||||
<div class="flex flex-col space-y-1.5 p-6">
|
<div class="flex flex-row justify-between items-center p-6">
|
||||||
<h3 class="whitespace-nowrap text-2xl font-semibold leading-none tracking-tight">Storage Usage
|
<h3 class="whitespace-nowrap text-2xl font-semibold leading-none tracking-tight">Storage Usage</h3>
|
||||||
</h3>
|
<svg class="w-4 h-4 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="22" x2="2" y1="12" y2="12"></line>
|
||||||
|
<path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path>
|
||||||
|
<line x1="6" x2="6.01" y1="16" y2="16"></line>
|
||||||
|
<line x1="10" x2="10.01" y1="16" y2="16"></line>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-6 grid gap-4">
|
<div class="p-6 grid gap-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="text-2xl font-bold">{allowance.AllowanceUsedByte} / {allowance.AllowanceByte}</div>
|
||||||
<span>Used</span>
|
|
||||||
<span>42.0GB</span>
|
|
||||||
</div>
|
|
||||||
<div class="w-full bg-gray-300 rounded-full h-2.5">
|
<div class="w-full bg-gray-300 rounded-full h-2.5">
|
||||||
<div class="bg-gray-800 h-2.5 rounded-full" style="width: 45%"></div>
|
<div class="bg-gray-800 h-2.5 rounded-full" id="allowanceProgress" style="width: 0%;"></div>
|
||||||
</div>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<span>Available</span>
|
|
||||||
<span>6.9GB</span>
|
|
||||||
</div>
|
|
||||||
<div class="w-full bg-gray-300 rounded-full h-2.5">
|
|
||||||
<div class="bg-gray-800 h-2.5 rounded-full" style="width: 100%"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
|
class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
|
||||||
@ -291,6 +286,7 @@ templ content(message types.Message, title string, user types.User, ListSession
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
@templ.JSONScript("AllowanceUsedPercent", allowance.AllowanceUsedPercent)
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.getElementById('currentPassword').addEventListener('input', function() {
|
document.getElementById('currentPassword').addEventListener('input', function() {
|
||||||
var validationBox = document.getElementById('validationBox');
|
var validationBox = document.getElementById('validationBox');
|
||||||
@ -300,6 +296,11 @@ templ content(message types.Message, title string, user types.User, ListSession
|
|||||||
validationBox.classList.add('hidden');
|
validationBox.classList.add('hidden');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let allowanceProgress = document.getElementById(`allowanceProgress`);
|
||||||
|
const AllowanceUsedPercent = JSON.parse(document.getElementById('AllowanceUsedPercent').textContent);
|
||||||
|
allowanceProgress.style.width = `${AllowanceUsedPercent}%`;
|
||||||
|
console.log(AllowanceUsedPercent)
|
||||||
</script>
|
</script>
|
||||||
<script src="/public/validatePassword.js" />
|
<script src="/public/validatePassword.js" />
|
||||||
@layout.Footer()
|
@layout.Footer()
|
||||||
@ -331,6 +332,6 @@ templ SessionTable(ListSession []*session.SessionInfo){
|
|||||||
</tbody>
|
</tbody>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ Main(title string, user types.User, ListSession []*session.SessionInfo, message types.Message) {
|
templ Main(title string, user types.User, allowance *types.Allowance, ListSession []*session.SessionInfo, message types.Message) {
|
||||||
@content(message, title, user, ListSession)
|
@content(message, title, user, allowance, ListSession)
|
||||||
}
|
}
|
Reference in New Issue
Block a user