Add User route and Refactor code
This commit is contained in:
parent
519749fd3a
commit
b311a41dc7
24 changed files with 902 additions and 489 deletions
|
|
@ -147,7 +147,7 @@ export class RecipeService {
|
||||||
|
|
||||||
getRecipeCountries(): Observable<string[]> {
|
getRecipeCountries(): Observable<string[]> {
|
||||||
return this._httpClient
|
return this._httpClient
|
||||||
.get<string[]>(environment.api + '/recipes/versions', {
|
.get<string[]>(environment.api + '/recipes/countries', {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
})
|
})
|
||||||
|
|
@ -156,7 +156,7 @@ export class RecipeService {
|
||||||
|
|
||||||
getRecipeFiles(country: string): Observable<string[]> {
|
getRecipeFiles(country: string): Observable<string[]> {
|
||||||
return this._httpClient
|
return this._httpClient
|
||||||
.get<string[]>(environment.api + '/recipes/versions/' + country, {
|
.get<string[]>(environment.api + '/recipes/' + country + '/versions', {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
})
|
})
|
||||||
|
|
|
||||||
6
server/contracts/common.go
Normal file
6
server/contracts/common.go
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package contracts
|
||||||
|
|
||||||
|
type ResponseDefault struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
34
server/contracts/user.go
Normal file
34
server/contracts/user.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package contracts
|
||||||
|
|
||||||
|
import "recipe-manager/enums/permissions"
|
||||||
|
|
||||||
|
// ================================== Users ==================================
|
||||||
|
|
||||||
|
type CreateUserReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Permissions permissions.Permission `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================== User ==================================
|
||||||
|
|
||||||
|
type UpdateUserNameReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserPermissionsReq struct {
|
||||||
|
Permissions permissions.Permission `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserPictureReq struct {
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRes struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Permissions permissions.Permission `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"recipe-manager/helpers"
|
"recipe-manager/helpers"
|
||||||
|
|
@ -12,10 +11,6 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
Log = logger.GetInstance()
|
|
||||||
)
|
|
||||||
|
|
||||||
type RecipeWithTimeStamps struct {
|
type RecipeWithTimeStamps struct {
|
||||||
Recipe models.Recipe
|
Recipe models.Recipe
|
||||||
TimeStamps int64
|
TimeStamps int64
|
||||||
|
|
@ -28,9 +23,10 @@ type Data struct {
|
||||||
currentRecipe *models.Recipe
|
currentRecipe *models.Recipe
|
||||||
recipeMap map[string]RecipeWithTimeStamps
|
recipeMap map[string]RecipeWithTimeStamps
|
||||||
Countries []helpers.CountryName
|
Countries []helpers.CountryName
|
||||||
|
taoLogger *logger.TaoLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewData() *Data {
|
func NewData(taoLogger *logger.TaoLogger) *Data {
|
||||||
|
|
||||||
countries := []helpers.CountryName{{
|
countries := []helpers.CountryName{{
|
||||||
CountryID: "tha",
|
CountryID: "tha",
|
||||||
|
|
@ -66,6 +62,7 @@ func NewData() *Data {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Countries: countries,
|
Countries: countries,
|
||||||
|
taoLogger: taoLogger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +88,7 @@ func (d *Data) GetRecipe(countryID, filename string) *models.Recipe {
|
||||||
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.GetInstance().Error("Error when read recipe file", zap.Error(err))
|
d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err))
|
||||||
return d.currentRecipe
|
return d.currentRecipe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +141,7 @@ func (d *Data) GetRecipe01ByProductCode(filename, countryID, productCode string)
|
||||||
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.GetInstance().Error("Error when read recipe file", zap.Error(err))
|
d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err))
|
||||||
for _, v := range d.currentRecipe.Recipe01 {
|
for _, v := range d.currentRecipe.Recipe01 {
|
||||||
if v.ProductCode == productCode {
|
if v.ProductCode == productCode {
|
||||||
return v, nil
|
return v, nil
|
||||||
|
|
@ -216,7 +213,7 @@ func (d *Data) GetMaterialSetting(countryID, filename string) []models.MaterialS
|
||||||
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.GetInstance().Error("Error when read recipe file", zap.Error(err))
|
d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err))
|
||||||
copy(result, d.currentRecipe.MaterialSetting)
|
copy(result, d.currentRecipe.MaterialSetting)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +257,7 @@ func (d *Data) GetMaterialCode(ids []uint64, countryID, filename string) []model
|
||||||
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
recipe, err := helpers.ReadRecipeFile(countryID, filename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.GetInstance().Error("Error when read recipe file", zap.Error(err))
|
d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err))
|
||||||
return d.currentRecipe.MaterialCode
|
return d.currentRecipe.MaterialCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,13 +323,3 @@ func (d *Data) GetCountryIDByName(countryName string) (string, error) {
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("country name: %s not found", countryName)
|
return "", fmt.Errorf("country name: %s not found", countryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Data) ExportToJSON() []byte {
|
|
||||||
b_recipe, err := json.Marshal(d.currentRecipe)
|
|
||||||
if err != nil {
|
|
||||||
Log.Error("Error when marshal recipe", zap.Error(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return b_recipe
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
-- slqlite3
|
-- slqlite3
|
||||||
-- create users table
|
-- create users table
|
||||||
CREATE TABLE users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
email TEXT NOT NULL,
|
email TEXT UNIQUE NOT NULL,
|
||||||
password TEXT NOT NULL,
|
permissions INT DEFAULT 0,
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE users DROP COLUMN picture;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE users ADD COLUMN picture TEXT;
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package data
|
package data
|
||||||
|
|
||||||
import "github.com/jmoiron/sqlx"
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
func NewSqliteDatabase() *sqlx.DB {
|
func NewSqliteDatabase() *sqlx.DB {
|
||||||
db := sqlx.MustConnect("sqlite3", "./data/database.db")
|
db := sqlx.MustConnect("sqlite3", "./data/database.db")
|
||||||
|
|
|
||||||
16
server/enums/permissions/permission.go
Normal file
16
server/enums/permissions/permission.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
type Permission int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ThaiPermission Permission = 1 << iota
|
||||||
|
MalayPermission
|
||||||
|
AusPermission
|
||||||
|
// NOTE: Add more permission here
|
||||||
|
|
||||||
|
SuperAdmin
|
||||||
|
)
|
||||||
|
|
||||||
|
func (userPermissions Permission) IsHavePermission(requiredPermissions Permission) bool {
|
||||||
|
return (userPermissions & requiredPermissions) == requiredPermissions
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"recipe-manager/services/logger"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func pull_request() error {
|
func pull_request() error {
|
||||||
|
|
@ -22,7 +19,7 @@ func pull_request() error {
|
||||||
|
|
||||||
if len(output) > 0 {
|
if len(output) > 0 {
|
||||||
if string(output) == "Already up to date." || string(output) == "Coffee recipe updated." {
|
if string(output) == "Already up to date." || string(output) == "Coffee recipe updated." {
|
||||||
logger.GetInstance().Info("Git pull successful", zap.String("output", string(output)))
|
//logger.GetInstance().Info("Git pull successful", zap.String("output", string(output)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ go 1.21.1
|
||||||
require (
|
require (
|
||||||
github.com/go-chi/chi/v5 v5.0.10
|
github.com/go-chi/chi/v5 v5.0.10
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.18
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
golang.org/x/oauth2 v0.12.0
|
golang.org/x/oauth2 v0.12.0
|
||||||
google.golang.org/api v0.143.0
|
google.golang.org/api v0.143.0
|
||||||
)
|
)
|
||||||
|
|
@ -14,7 +18,7 @@ require (
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
github.com/google/uuid v1.3.1 // indirect
|
github.com/google/uuid v1.4.0
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
|
@ -24,12 +28,6 @@ require (
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
|
||||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.18 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
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/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
|
@ -134,8 +135,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
|
@ -164,6 +165,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
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/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
|
@ -176,6 +178,7 @@ github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4
|
||||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|
|
||||||
53
server/middlewares/authorized.go
Normal file
53
server/middlewares/authorized.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"net/http"
|
||||||
|
"recipe-manager/enums/permissions"
|
||||||
|
"recipe-manager/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Authorize(p []permissions.Permission, nextRoute http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := r.Context().Value("user").(*models.User)
|
||||||
|
|
||||||
|
for _, pm := range p {
|
||||||
|
if !user.Permissions.IsHavePermission(pm) {
|
||||||
|
// If not have permission response unauthorized
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
err := json.NewEncoder(w).Encode("Unauthorized")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextRoute.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OwnOrAuthorize(p []permissions.Permission, nextRoute http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
reqUserID := chi.URLParam(r, "id")
|
||||||
|
user := r.Context().Value("user").(*models.User)
|
||||||
|
|
||||||
|
if reqUserID == "" {
|
||||||
|
// If not have permission response unauthorized
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
err := json.NewEncoder(w).Encode("Unauthorized")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqUserID == user.ID {
|
||||||
|
nextRoute.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Authorize(p, nextRoute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "recipe-manager/enums/permissions"
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Name string `json:"name"`
|
ID string `json:"id"`
|
||||||
Email string `json:"email"`
|
Name string `json:"name"`
|
||||||
Picture string `json:"picture"`
|
Email string `json:"email"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Permissions permissions.Permission `json:"permissions"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"recipe-manager/config"
|
|
||||||
"recipe-manager/services/oauth"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"recipe-manager/config"
|
||||||
|
"recipe-manager/services/logger"
|
||||||
|
"recipe-manager/services/oauth"
|
||||||
|
"recipe-manager/services/user"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthRouter struct {
|
type AuthRouter struct {
|
||||||
cfg *config.ServerConfig
|
cfg *config.ServerConfig
|
||||||
oauth oauth.OAuthService
|
oauth oauth.OAuthService
|
||||||
}
|
userService user.UserService
|
||||||
|
taoLogger *logger.TaoLogger
|
||||||
func NewAuthRouter(cfg *config.ServerConfig, oauth oauth.OAuthService) *AuthRouter {
|
|
||||||
return &AuthRouter{cfg, oauth}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ar *AuthRouter) Route(r chi.Router) {
|
func (ar *AuthRouter) Route(r chi.Router) {
|
||||||
|
|
@ -29,7 +30,10 @@ func (ar *AuthRouter) Route(r chi.Router) {
|
||||||
|
|
||||||
// generate state and nonce
|
// generate state and nonce
|
||||||
bytes := make([]byte, 32)
|
bytes := make([]byte, 32)
|
||||||
rand.Read(bytes)
|
_, err := rand.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
state := base64.URLEncoding.EncodeToString(bytes)
|
state := base64.URLEncoding.EncodeToString(bytes)
|
||||||
|
|
||||||
stateMap := map[string]string{}
|
stateMap := map[string]string{}
|
||||||
|
|
@ -38,14 +42,16 @@ func (ar *AuthRouter) Route(r chi.Router) {
|
||||||
stateMap["redirect_to"] = r.URL.Query().Get("redirect_to")
|
stateMap["redirect_to"] = r.URL.Query().Get("redirect_to")
|
||||||
}
|
}
|
||||||
|
|
||||||
url := ar.oauth.AuthURL(state, stateMap)
|
authURL := ar.oauth.AuthURL(state, stateMap)
|
||||||
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, authURL, http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/google/callback", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/google/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||||
// check state
|
// check state
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
var redirect_to string
|
var redirectTo string
|
||||||
state := r.URL.Query().Get("state")
|
state := r.URL.Query().Get("state")
|
||||||
if state == "" {
|
if state == "" {
|
||||||
http.Error(w, "State not found", http.StatusBadRequest)
|
http.Error(w, "State not found", http.StatusBadRequest)
|
||||||
|
|
@ -58,7 +64,7 @@ func (ar *AuthRouter) Route(r chi.Router) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect_to = val["redirect_to"]
|
redirectTo = val["redirect_to"]
|
||||||
|
|
||||||
ar.oauth.RemoveState(state)
|
ar.oauth.RemoveState(state)
|
||||||
}
|
}
|
||||||
|
|
@ -70,25 +76,44 @@ func (ar *AuthRouter) Route(r chi.Router) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get user info
|
// get userInfo info
|
||||||
user, err := ar.oauth.GetUserInfo(r.Context(), token)
|
userInfo, err := ar.oauth.GetUserInfo(r.Context(), token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Error getting user info", http.StatusBadRequest)
|
http.Error(w, "Error getting userInfo info", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// map with database
|
||||||
|
userFromDb, err := ar.userService.GetUserByEmail(ctx, userInfo.Email)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Error while getting user data from database.", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if userFromDb == nil {
|
||||||
|
http.Error(w, "Unauthorized, We not found your email, Please contact admin.", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
picture := userInfo.Picture
|
||||||
|
if userFromDb.Picture != "" {
|
||||||
|
picture = userFromDb.Picture
|
||||||
|
}
|
||||||
|
|
||||||
value := url.Values{
|
value := url.Values{
|
||||||
"name": {user.Name},
|
"id": {userFromDb.ID},
|
||||||
"email": {user.Email},
|
"name": {userFromDb.Name},
|
||||||
"picture": {user.Picture},
|
"email": {userInfo.Email},
|
||||||
|
"picture": {picture},
|
||||||
|
"permissions": {strconv.Itoa(int(userFromDb.Permissions))},
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirect_to != "" {
|
if redirectTo != "" {
|
||||||
value.Add("redirect_to", redirect_to)
|
value.Add("redirect_to", redirectTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info("User Log-In Success", zap.String("user", user.Name), zap.String("email", user.Email))
|
ar.taoLogger.Log.Info("User Log-In Success", zap.String("userInfo", userInfo.Name), zap.String("email", userInfo.Email))
|
||||||
|
|
||||||
// redirect to frontend with token and refresh token
|
// redirect to frontend with token and refresh token
|
||||||
w.Header().Add("set-cookie", "access_token="+token.AccessToken+"; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=3600")
|
w.Header().Add("set-cookie", "access_token="+token.AccessToken+"; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=3600")
|
||||||
|
|
@ -132,38 +157,9 @@ func (ar *AuthRouter) Route(r chi.Router) {
|
||||||
w.Header().Add("set-cookie", "refresh_token=; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=0")
|
w.Header().Add("set-cookie", "refresh_token=; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=0")
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/user", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
token := &oauth2.Token{}
|
|
||||||
if cookie, err := r.Cookie("access_token"); err == nil {
|
|
||||||
token.AccessToken = cookie.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// if have refresh token, set refresh token to token
|
|
||||||
if cookie, err := r.Cookie("refresh_token"); err == nil {
|
|
||||||
token.RefreshToken = cookie.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
if token.AccessToken == "" && token.RefreshToken == "" {
|
|
||||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get user info
|
|
||||||
user, err := ar.oauth.GetUserInfo(r.Context(), token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Error getting user info", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// return user info
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
||||||
"user": user,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewAuthRouter(cfg *config.ServerConfig, oauth oauth.OAuthService, userService user.UserService, taoLogger *logger.TaoLogger) *AuthRouter {
|
||||||
|
return &AuthRouter{cfg, oauth, userService, taoLogger}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package routers
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
@ -24,172 +25,36 @@ type RecipeRouter struct {
|
||||||
data *data.Data
|
data *data.Data
|
||||||
sheetService sheet.SheetService
|
sheetService sheet.SheetService
|
||||||
recipeService recipe.RecipeService
|
recipeService recipe.RecipeService
|
||||||
|
taoLogger *logger.TaoLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func NewRecipeRouter(data *data.Data, recipeService recipe.RecipeService, sheetService sheet.SheetService, taoLogger *logger.TaoLogger) *RecipeRouter {
|
||||||
Log = logger.GetInstance()
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewRecipeRouter(data *data.Data, recipeService recipe.RecipeService, sheetService sheet.SheetService) *RecipeRouter {
|
|
||||||
return &RecipeRouter{
|
return &RecipeRouter{
|
||||||
data: data,
|
data,
|
||||||
recipeService: recipeService,
|
sheetService,
|
||||||
sheetService: sheetService,
|
recipeService,
|
||||||
|
taoLogger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *RecipeRouter) Route(r chi.Router) {
|
func (rr *RecipeRouter) Route(r chi.Router) {
|
||||||
r.Route("/recipes", func(r chi.Router) {
|
r.Route("/recipes", func(r chi.Router) {
|
||||||
r.Get("/dashboard", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/dashboard", rr.dashBoard)
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
|
|
||||||
country := r.URL.Query().Get("country")
|
r.Get("/overview", rr.overview)
|
||||||
filename := r.URL.Query().Get("filename")
|
|
||||||
|
|
||||||
result, err := rr.recipeService.GetRecipeDashboard(&contracts.RecipeDashboardRequest{
|
r.Get("/{product_code}", rr.getRecipeByProductCode)
|
||||||
Country: country,
|
|
||||||
Filename: filename,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
r.Get("/{product_code}/mat", rr.getRecipeMatByProductCode)
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(result)
|
r.Get("/{country}/{filename}/json", rr.getRecipeJson)
|
||||||
})
|
|
||||||
|
|
||||||
r.Get("/overview", func(w http.ResponseWriter, r *http.Request) {
|
r.Post("/edit/{country}/{filename}", rr.updateRecipe)
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
var take, offset uint64 = 10, 0
|
|
||||||
if newOffset, err := strconv.ParseUint(r.URL.Query().Get("offset"), 10, 64); err == nil {
|
|
||||||
offset = newOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
if newTake, err := strconv.ParseUint(r.URL.Query().Get("take"), 10, 64); err == nil {
|
r.Get("/countries", func(w http.ResponseWriter, r *http.Request) {
|
||||||
take = newTake
|
|
||||||
}
|
|
||||||
|
|
||||||
country := r.URL.Query().Get("country")
|
|
||||||
filename := r.URL.Query().Get("filename")
|
|
||||||
materialIds := r.URL.Query().Get("materialIds")
|
|
||||||
|
|
||||||
var materialIdsUint []int
|
|
||||||
for _, v := range strings.Split(materialIds, ",") {
|
|
||||||
materialIdUint, err := strconv.ParseUint(v, 10, 64)
|
|
||||||
if err != nil || materialIdUint == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
materialIdsUint = append(materialIdsUint, int(materialIdUint))
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := rr.recipeService.GetRecipeOverview(&contracts.RecipeOverviewRequest{
|
|
||||||
Take: int(take),
|
|
||||||
Skip: int(offset),
|
|
||||||
Search: r.URL.Query().Get("search"),
|
|
||||||
Country: country,
|
|
||||||
Filename: filename,
|
|
||||||
MatIds: materialIdsUint,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Get("/{product_code}", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
productCode := chi.URLParam(r, "product_code")
|
|
||||||
|
|
||||||
// recipe := rr.data.GetRecipe01()
|
|
||||||
// recipeMetaData := rr.sheetService.GetSheet(r.Context(), "1rSUKcc5POR1KeZFGoeAZIoVoI7LPGztBhPw5Z_ConDE")
|
|
||||||
|
|
||||||
// var recipeResult *models.Recipe01
|
|
||||||
// recipeMetaDataResult := map[string]string{}
|
|
||||||
|
|
||||||
// for _, v := range recipe {
|
|
||||||
// if v.ProductCode == productCode {
|
|
||||||
// recipeResult = &v
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for _, v := range recipeMetaData {
|
|
||||||
// if v[0].(string) == productCode {
|
|
||||||
// recipeMetaDataResult = map[string]string{
|
|
||||||
// "productCode": v[0].(string),
|
|
||||||
// "name": v[1].(string),
|
|
||||||
// "otherName": v[2].(string),
|
|
||||||
// "description": v[3].(string),
|
|
||||||
// "otherDescription": v[4].(string),
|
|
||||||
// "picture": v[5].(string),
|
|
||||||
// }
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if recipeResult == nil {
|
|
||||||
// http.Error(w, "Not Found", http.StatusNotFound)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// json.NewEncoder(w).Encode(map[string]interface{}{
|
|
||||||
// "recipe": recipeResult,
|
|
||||||
// "recipeMetaData": recipeMetaDataResult,
|
|
||||||
// })
|
|
||||||
|
|
||||||
result, err := rr.recipeService.GetRecipeDetail(&contracts.RecipeDetailRequest{
|
|
||||||
Filename: r.URL.Query().Get("filename"),
|
|
||||||
Country: r.URL.Query().Get("country"),
|
|
||||||
ProductCode: productCode,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Get("/{product_code}/mat", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
productCode := chi.URLParam(r, "product_code")
|
|
||||||
|
|
||||||
result, err := rr.recipeService.GetRecipeDetailMat(&contracts.RecipeDetailRequest{
|
|
||||||
Filename: r.URL.Query().Get("filename"),
|
|
||||||
Country: r.URL.Query().Get("country"),
|
|
||||||
ProductCode: productCode,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Get("/{country}/{filename}/json", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
country := chi.URLParam(r, "country")
|
|
||||||
filename := chi.URLParam(r, "filename")
|
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
countryID, err := rr.data.GetCountryIDByName(country)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", country), http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
json.NewEncoder(w).Encode(rr.data.GetRecipe(countryID, filename))
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Get("/versions", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
// get key from map
|
// get key from map
|
||||||
keys := []string{}
|
var keys []string
|
||||||
for k := range rr.data.AllRecipeFiles {
|
for k := range rr.data.AllRecipeFiles {
|
||||||
countryName, err := rr.data.GetCountryNameByID(k)
|
countryName, err := rr.data.GetCountryNameByID(k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -197,10 +62,14 @@ func (rr *RecipeRouter) Route(r chi.Router) {
|
||||||
}
|
}
|
||||||
keys = append(keys, countryName)
|
keys = append(keys, countryName)
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(keys)
|
if err := json.NewEncoder(w).Encode(keys); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetCountryRecipe", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/versions/{country}", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/{country}/versions", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
|
||||||
countryName := chi.URLParam(r, "country")
|
countryName := chi.URLParam(r, "country")
|
||||||
|
|
@ -209,17 +78,22 @@ func (rr *RecipeRouter) Route(r chi.Router) {
|
||||||
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", countryName), http.StatusNotFound)
|
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", countryName), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
files := []string{}
|
var files []string
|
||||||
for _, v := range rr.data.AllRecipeFiles[countryID] {
|
for _, v := range rr.data.AllRecipeFiles[countryID] {
|
||||||
files = append(files, v.Name)
|
files = append(files, v.Name)
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(files)
|
|
||||||
|
if err := json.NewEncoder(w).Encode(files); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetVersionsCountryRecipe", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/test/sheet", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/test/sheet", func(w http.ResponseWriter, r *http.Request) {
|
||||||
result := rr.sheetService.GetSheet(r.Context(), "1rSUKcc5POR1KeZFGoeAZIoVoI7LPGztBhPw5Z_ConDE")
|
result := rr.sheetService.GetSheet(r.Context(), "1rSUKcc5POR1KeZFGoeAZIoVoI7LPGztBhPw5Z_ConDE")
|
||||||
|
|
||||||
mapResult := []map[string]string{}
|
var mapResult []map[string]string
|
||||||
|
|
||||||
for _, v := range result {
|
for _, v := range result {
|
||||||
mapResult = append(mapResult, map[string]string{
|
mapResult = append(mapResult, map[string]string{
|
||||||
|
|
@ -231,96 +105,289 @@ func (rr *RecipeRouter) Route(r chi.Router) {
|
||||||
"picture": v[5].(string),
|
"picture": v[5].(string),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(mapResult)
|
if err := json.NewEncoder(w).Encode(mapResult); err != nil {
|
||||||
})
|
rr.taoLogger.Log.Error("RecipeRouter.TestSheet", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
r.Post("/edit/{country}/{filename}", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
Log.Debug("Edit: ", zap.String("path", r.RequestURI))
|
|
||||||
filename := chi.URLParam(r, "filename")
|
|
||||||
country := chi.URLParam(r, "country")
|
|
||||||
|
|
||||||
countryID, err := rr.data.GetCountryIDByName(country)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", country), http.StatusNotFound)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target_recipe := rr.data.GetRecipe(countryID, filename)
|
|
||||||
|
|
||||||
Log.Debug("Target => ", zap.Any("target", target_recipe.MachineSetting.ConfigNumber))
|
|
||||||
|
|
||||||
// check request structure
|
|
||||||
|
|
||||||
// FIXME: Request structure bug. Case-sensitive, likely bug at client
|
|
||||||
// uncomment the below code to view the bug
|
|
||||||
|
|
||||||
// var change_request map[string]interface{}
|
|
||||||
// err = json.NewDecoder(r.Body).Decode(&change_request)
|
|
||||||
// if err != nil {
|
|
||||||
// Log.Error("Decode in request failed: ", zap.Error(err))
|
|
||||||
// }
|
|
||||||
// Log.Debug("Request => ", zap.Any("request", change_request))
|
|
||||||
|
|
||||||
// Body
|
|
||||||
var changes models.Recipe01
|
|
||||||
err = json.NewDecoder(r.Body).Decode(&changes)
|
|
||||||
if err != nil {
|
|
||||||
Log.Error("Decode in request failed: ", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Debug("Changes: ", zap.Any("changes", changes))
|
|
||||||
// TODO: find the matched pd
|
|
||||||
target_menu, err := rr.data.GetRecipe01ByProductCode(filename, countryID, changes.ProductCode)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Log.Error("Error when get recipe by product code", zap.Error(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
menu_map := target_menu.ToMap()
|
|
||||||
change_map := changes.ToMap()
|
|
||||||
|
|
||||||
// Find changes
|
|
||||||
for key, val := range menu_map {
|
|
||||||
|
|
||||||
test_bool, err := helpers.DynamicCompare(val, change_map[key])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Log.Error("DynamicCompare in request failed: ", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !test_bool {
|
|
||||||
menu_map[key] = change_map[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply changes
|
|
||||||
tempRecipe := models.Recipe01{}
|
|
||||||
tempRecipe = tempRecipe.FromMap(menu_map)
|
|
||||||
rr.data.SetValuesToRecipe(tempRecipe)
|
|
||||||
Log.Debug("ApplyChange", zap.Any("status", "passed"))
|
|
||||||
|
|
||||||
// check if changed
|
|
||||||
// Log.Debug("Check if changed", zap.Any("result", rr.data.GetRecipe01ByProductCode(changes.ProductCode)))
|
|
||||||
|
|
||||||
file, _ := os.Create(path.Join("./cofffeemachineConfig", countryID, filename))
|
|
||||||
if err != nil {
|
|
||||||
Log.Error("Error when tried to create file", zap.Error(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(file)
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
err = encoder.Encode(rr.data.GetRecipe(countryID, filename))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
Log.Error("Error when write file", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
||||||
"status": "OK",
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== Handler =================================
|
||||||
|
|
||||||
|
func (rr *RecipeRouter) dashBoard(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
country := r.URL.Query().Get("country")
|
||||||
|
filename := r.URL.Query().Get("filename")
|
||||||
|
|
||||||
|
result, err := rr.recipeService.GetRecipeDashboard(&contracts.RecipeDashboardRequest{
|
||||||
|
Country: country,
|
||||||
|
Filename: filename,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.Dashboard", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RecipeRouter) overview(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
var take, offset uint64 = 10, 0
|
||||||
|
if newOffset, err := strconv.ParseUint(r.URL.Query().Get("offset"), 10, 64); err == nil {
|
||||||
|
offset = newOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
if newTake, err := strconv.ParseUint(r.URL.Query().Get("take"), 10, 64); err == nil {
|
||||||
|
take = newTake
|
||||||
|
}
|
||||||
|
|
||||||
|
country := r.URL.Query().Get("country")
|
||||||
|
filename := r.URL.Query().Get("filename")
|
||||||
|
materialIds := r.URL.Query().Get("materialIds")
|
||||||
|
|
||||||
|
var materialIdsUint []int
|
||||||
|
for _, v := range strings.Split(materialIds, ",") {
|
||||||
|
materialIdUint, err := strconv.ParseUint(v, 10, 64)
|
||||||
|
if err != nil || materialIdUint == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
materialIdsUint = append(materialIdsUint, int(materialIdUint))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := rr.recipeService.GetRecipeOverview(&contracts.RecipeOverviewRequest{
|
||||||
|
Take: int(take),
|
||||||
|
Skip: int(offset),
|
||||||
|
Search: r.URL.Query().Get("search"),
|
||||||
|
Country: country,
|
||||||
|
Filename: filename,
|
||||||
|
MatIds: materialIdsUint,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.Overview", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RecipeRouter) getRecipeByProductCode(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
productCode := chi.URLParam(r, "product_code")
|
||||||
|
fileName := r.URL.Query().Get("filename")
|
||||||
|
country := r.URL.Query().Get("country")
|
||||||
|
|
||||||
|
// recipe := rr.data.GetRecipe01()
|
||||||
|
// recipeMetaData := rr.sheetService.GetSheet(r.Context(), "1rSUKcc5POR1KeZFGoeAZIoVoI7LPGztBhPw5Z_ConDE")
|
||||||
|
|
||||||
|
// var recipeResult *models.Recipe01
|
||||||
|
// recipeMetaDataResult := map[string]string{}
|
||||||
|
|
||||||
|
// for _, v := range recipe {
|
||||||
|
// if v.ProductCode == productCode {
|
||||||
|
// recipeResult = &v
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for _, v := range recipeMetaData {
|
||||||
|
// if v[0].(string) == productCode {
|
||||||
|
// recipeMetaDataResult = map[string]string{
|
||||||
|
// "productCode": v[0].(string),
|
||||||
|
// "name": v[1].(string),
|
||||||
|
// "otherName": v[2].(string),
|
||||||
|
// "description": v[3].(string),
|
||||||
|
// "otherDescription": v[4].(string),
|
||||||
|
// "picture": v[5].(string),
|
||||||
|
// }
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if recipeResult == nil {
|
||||||
|
// http.Error(w, "Not Found", http.StatusNotFound)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
// "recipe": recipeResult,
|
||||||
|
// "recipeMetaData": recipeMetaDataResult,
|
||||||
|
// })
|
||||||
|
|
||||||
|
result, err := rr.recipeService.GetRecipeDetail(&contracts.RecipeDetailRequest{
|
||||||
|
Filename: fileName,
|
||||||
|
Country: country,
|
||||||
|
ProductCode: productCode,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetRecipeByProductCode", zap.Error(err))
|
||||||
|
http.Error(w, fmt.Sprintf("Recipe file: %s with productCode: %s not found", fileName, productCode), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetRecipeByProductCode", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RecipeRouter) getRecipeMatByProductCode(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
productCode := chi.URLParam(r, "product_code")
|
||||||
|
fileName := r.URL.Query().Get("filename")
|
||||||
|
country := r.URL.Query().Get("country")
|
||||||
|
|
||||||
|
result, err := rr.recipeService.GetRecipeDetailMat(&contracts.RecipeDetailRequest{
|
||||||
|
Filename: fileName,
|
||||||
|
Country: country,
|
||||||
|
ProductCode: productCode,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetRecipeMatByProductCode", zap.Error(err))
|
||||||
|
http.Error(w, fmt.Sprintf("Material Recipe file: %s with productCode: %s not found", fileName, productCode), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetRecipeMatByProductCode", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RecipeRouter) getRecipeJson(w http.ResponseWriter, r *http.Request) {
|
||||||
|
country := chi.URLParam(r, "country")
|
||||||
|
filename := chi.URLParam(r, "filename")
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
countryID, err := rr.data.GetCountryIDByName(country)
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetRecipeJson", zap.Error(err))
|
||||||
|
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", country), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := json.NewEncoder(w).Encode(rr.data.GetRecipe(countryID, filename)); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.GetRecipeJson", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RecipeRouter) updateRecipe(w http.ResponseWriter, r *http.Request) {
|
||||||
|
rr.taoLogger.Log.Debug("Edit: ", zap.String("path", r.RequestURI))
|
||||||
|
filename := chi.URLParam(r, "filename")
|
||||||
|
country := chi.URLParam(r, "country")
|
||||||
|
|
||||||
|
countryID, err := rr.data.GetCountryIDByName(country)
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(err))
|
||||||
|
http.Error(w, fmt.Sprintf("Country Name: %s not found!!!", country), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetRecipe := rr.data.GetRecipe(countryID, filename)
|
||||||
|
|
||||||
|
rr.taoLogger.Log.Debug("Target => ", zap.Any("target", targetRecipe.MachineSetting.ConfigNumber))
|
||||||
|
|
||||||
|
// check request structure
|
||||||
|
|
||||||
|
// FIXME: Request structure bug. Case-sensitive, likely bug at client
|
||||||
|
// uncomment the below code to view the bug
|
||||||
|
|
||||||
|
// var change_request map[string]interface{}
|
||||||
|
// err = json.NewDecoder(r.Body).Decode(&change_request)
|
||||||
|
// if err != nil {
|
||||||
|
// rr.taoLogger.Log.Error("Decode in request failed: ", zap.Error(err))
|
||||||
|
// }
|
||||||
|
// Log.Debug("Request => ", zap.Any("request", change_request))
|
||||||
|
|
||||||
|
// Body
|
||||||
|
var changes models.Recipe01
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&changes)
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Decode in request failed")))
|
||||||
|
http.Error(w, "Can't Decode Recipe request body.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rr.taoLogger.Log.Debug("Changes: ", zap.Any("changes", changes))
|
||||||
|
// TODO: find the matched pd
|
||||||
|
targetMenu, err := rr.data.GetRecipe01ByProductCode(filename, countryID, changes.ProductCode)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when get recipe by product code")))
|
||||||
|
http.Error(w, fmt.Sprintf("Recipe country: %s file: %s productCode: %s not found.", country, filename, changes.ProductCode), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
menuMap := targetMenu.ToMap()
|
||||||
|
changeMap := changes.ToMap()
|
||||||
|
|
||||||
|
// Find changes
|
||||||
|
for key, val := range menuMap {
|
||||||
|
|
||||||
|
testBool, err := helpers.DynamicCompare(val, changeMap[key])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "DynamicCompare in request failed")))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testBool {
|
||||||
|
menuMap[key] = changeMap[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply changes
|
||||||
|
tempRecipe := models.Recipe01{}
|
||||||
|
tempRecipe = tempRecipe.FromMap(menuMap)
|
||||||
|
rr.data.SetValuesToRecipe(tempRecipe)
|
||||||
|
rr.taoLogger.Log.Debug("ApplyChange", zap.Any("status", "passed"))
|
||||||
|
|
||||||
|
// check if changed
|
||||||
|
// Log.Debug("Check if changed", zap.Any("result", rr.data.GetRecipe01ByProductCode(changes.ProductCode)))
|
||||||
|
|
||||||
|
file, _ := os.Create(path.Join("./cofffeemachineConfig", countryID, filename))
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when tried to create file")))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(file)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
err = encoder.Encode(rr.data.GetRecipe(countryID, filename))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(errors.WithMessage(err, "Error when write file")))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(&contracts.ResponseDefault{
|
||||||
|
Status: http.StatusText(http.StatusOK),
|
||||||
|
Message: "Recipe Updated",
|
||||||
|
}); err != nil {
|
||||||
|
rr.taoLogger.Log.Error("RecipeRouter.UpdateRecipe", zap.Error(err))
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
98
server/routers/user.go
Normal file
98
server/routers/user.go
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"net/http"
|
||||||
|
"recipe-manager/contracts"
|
||||||
|
"recipe-manager/enums/permissions"
|
||||||
|
"recipe-manager/middlewares"
|
||||||
|
"recipe-manager/services/logger"
|
||||||
|
"recipe-manager/services/user"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRouter struct {
|
||||||
|
taoLogger *logger.TaoLogger
|
||||||
|
userService user.UserService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserRouter(taoLogger *logger.TaoLogger, userService user.UserService) *UserRouter {
|
||||||
|
return &UserRouter{taoLogger, userService}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ur *UserRouter) Route(r chi.Router) {
|
||||||
|
|
||||||
|
// Users
|
||||||
|
r.Route("/users", func(r chi.Router) {
|
||||||
|
r.Get("/", middlewares.Authorize([]permissions.Permission{permissions.SuperAdmin}, ur.getUsers))
|
||||||
|
|
||||||
|
r.Post("/", middlewares.Authorize([]permissions.Permission{permissions.SuperAdmin}, ur.createUser))
|
||||||
|
})
|
||||||
|
|
||||||
|
// User
|
||||||
|
r.Route("/user", func(r chi.Router) {
|
||||||
|
r.Get("/{id}", middlewares.OwnOrAuthorize([]permissions.Permission{permissions.SuperAdmin}, ur.getUser))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================== Users Handler ================================
|
||||||
|
|
||||||
|
func (ur *UserRouter) getUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO: get all User, This route only SuperAdmin permission can access
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"status": "OK",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ur *UserRouter) createUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
u := &contracts.CreateUserReq{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(u); err != nil {
|
||||||
|
ur.taoLogger.Log.Error("UserRouter.CreateUser", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
ur.taoLogger.Log.Info("UserRouter.CreateUser", zap.Reflect("u", u))
|
||||||
|
|
||||||
|
if err := ur.userService.CreateNewUser(ctx, u.Name, u.Email, u.Picture, u.Permissions); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
if err := json.NewEncoder(w).Encode(&contracts.ResponseDefault{
|
||||||
|
Status: http.StatusText(http.StatusNoContent),
|
||||||
|
Message: "Created",
|
||||||
|
}); err != nil {
|
||||||
|
ur.taoLogger.Log.Error("UserRouter.CreateUser", zap.Error(err))
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== User Handler ===============================
|
||||||
|
|
||||||
|
func (ur *UserRouter) getUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
userID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
getUser, err := ur.userService.GetUserByID(ctx, userID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(&contracts.UserRes{
|
||||||
|
ID: getUser.ID,
|
||||||
|
Name: getUser.Name,
|
||||||
|
Email: getUser.Email,
|
||||||
|
Picture: getUser.Picture,
|
||||||
|
Permissions: getUser.Permissions,
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
205
server/server.go
205
server/server.go
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -12,33 +13,27 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"recipe-manager/config"
|
"recipe-manager/config"
|
||||||
"recipe-manager/data"
|
"recipe-manager/data"
|
||||||
|
"recipe-manager/enums/permissions"
|
||||||
"recipe-manager/models"
|
"recipe-manager/models"
|
||||||
"recipe-manager/routers"
|
"recipe-manager/routers"
|
||||||
"recipe-manager/services/logger"
|
"recipe-manager/services/logger"
|
||||||
"recipe-manager/services/oauth"
|
"recipe-manager/services/oauth"
|
||||||
"recipe-manager/services/recipe"
|
"recipe-manager/services/recipe"
|
||||||
"recipe-manager/services/sheet"
|
"recipe-manager/services/sheet"
|
||||||
|
"recipe-manager/services/user"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Log = logger.GetInstance()
|
pythonApiLock sync.Mutex
|
||||||
python_api_lock sync.Mutex
|
|
||||||
|
|
||||||
upgrader = websocket.Upgrader{
|
|
||||||
ReadBufferSize: 1024,
|
|
||||||
WriteBufferSize: 1024,
|
|
||||||
CheckOrigin: func(r *http.Request) bool { return true },
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadConfig(path string) (*config.ServerConfig, error) {
|
func loadConfig(path string) (*config.ServerConfig, error) {
|
||||||
|
|
@ -48,25 +43,27 @@ func loadConfig(path string) (*config.ServerConfig, error) {
|
||||||
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
var config config.ServerConfig
|
var serverConfig config.ServerConfig
|
||||||
err := viper.ReadInConfig()
|
err := viper.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = viper.Unmarshal(&config)
|
err = viper.Unmarshal(&serverConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &config, nil
|
return &serverConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
data *data.Data
|
data *data.Data
|
||||||
cfg *config.ServerConfig
|
database *sqlx.DB
|
||||||
oauth oauth.OAuthService
|
cfg *config.ServerConfig
|
||||||
|
oauth oauth.OAuthService
|
||||||
|
taoLogger *logger.TaoLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() *Server {
|
func NewServer() *Server {
|
||||||
|
|
@ -77,30 +74,33 @@ func NewServer() *Server {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
taoLogger := logger.NewTaoLogger(serverCfg)
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
server: &http.Server{Addr: fmt.Sprintf(":%d", serverCfg.ServerPort)},
|
server: &http.Server{Addr: fmt.Sprintf(":%d", serverCfg.ServerPort)},
|
||||||
data: data.NewData(),
|
data: data.NewData(taoLogger),
|
||||||
cfg: serverCfg,
|
database: data.NewSqliteDatabase(),
|
||||||
oauth: oauth.NewOAuthService(serverCfg),
|
cfg: serverCfg,
|
||||||
|
oauth: oauth.NewOAuthService(serverCfg),
|
||||||
|
taoLogger: taoLogger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Run() error {
|
func (s *Server) Run() error {
|
||||||
|
|
||||||
// logger
|
|
||||||
// defer log_inst.Sync()
|
|
||||||
|
|
||||||
if s.cfg.Debug {
|
|
||||||
// logger.SetLevel("DEBUG")
|
|
||||||
Log.Debug("Debug mode", zap.Bool("enable", s.cfg.Debug))
|
|
||||||
logger.EnableDebug(s.cfg.Debug)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go cli.CommandLineListener()
|
//go cli.CommandLineListener()
|
||||||
|
|
||||||
s.createHandler()
|
s.createHandler()
|
||||||
// log.Printf("Server running on %s", s.server.Addr)
|
// log.Printf("Server running on %s", s.server.Addr)
|
||||||
Log.Info("Server running", zap.String("addr", s.server.Addr))
|
s.taoLogger.Log.Info("Server running", zap.String("addr", s.server.Addr))
|
||||||
|
|
||||||
|
defer func(Log *zap.Logger) {
|
||||||
|
err := Log.Sync()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}(s.taoLogger.Log)
|
||||||
|
|
||||||
return s.server.ListenAndServe()
|
return s.server.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,15 +115,22 @@ func (s *Server) createHandler() {
|
||||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
database := data.NewData()
|
// Recipe Service
|
||||||
|
recipeService := recipe.NewRecipeService(s.data)
|
||||||
|
|
||||||
|
// User Service
|
||||||
|
userService := user.NewUserService(s.cfg, s.database, s.taoLogger)
|
||||||
|
|
||||||
|
// Seed
|
||||||
|
_ = userService.CreateNewUser(context.WithValue(context.Background(), "user", &models.User{Email: "system"}), "kenta420", "poomipat.c@forth.co.th", "", permissions.SuperAdmin)
|
||||||
|
|
||||||
// Auth Router
|
// Auth Router
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
ar := routers.NewAuthRouter(s.cfg, s.oauth)
|
ar := routers.NewAuthRouter(s.cfg, s.oauth, userService, s.taoLogger)
|
||||||
ar.Route(r)
|
ar.Route(r)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Protect Group
|
// Protected Group
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
|
||||||
r.Use(func(next http.Handler) http.Handler {
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
|
|
@ -134,7 +141,7 @@ func (s *Server) createHandler() {
|
||||||
token.AccessToken = cookie.Value
|
token.AccessToken = cookie.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := s.oauth.GetUserInfo(r.Context(), token)
|
userInfo, err := s.oauth.GetUserInfo(r.Context(), token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if have refresh token, set refresh token to token
|
// if have refresh token, set refresh token to token
|
||||||
|
|
@ -149,11 +156,38 @@ func (s *Server) createHandler() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userInfo, err = s.oauth.GetUserInfo(r.Context(), newToken)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// set new token to cookie
|
// set new token to cookie
|
||||||
w.Header().Add("set-cookie", fmt.Sprintf("access_token=%s; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=3600", newToken.AccessToken))
|
w.Header().Add("set-cookie", fmt.Sprintf("access_token=%s; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=3600", newToken.AccessToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.WithValue(r.Context(), "user", user)
|
if userInfo != nil {
|
||||||
|
userFromDB, err := userService.GetUserByEmail(r.Context(), userInfo.Email)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if userFromDB != nil {
|
||||||
|
userInfo.ID = userFromDB.ID
|
||||||
|
userInfo.Name = userFromDB.Name
|
||||||
|
if userFromDB.Picture != "" {
|
||||||
|
userInfo.Picture = userFromDB.Picture
|
||||||
|
}
|
||||||
|
userInfo.Permissions = userFromDB.Permissions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.WithValue(r.Context(), "user", userInfo)
|
||||||
|
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
|
|
@ -163,18 +197,18 @@ func (s *Server) createHandler() {
|
||||||
|
|
||||||
// locking
|
// locking
|
||||||
if !pyAPIhandler(w, r) {
|
if !pyAPIhandler(w, r) {
|
||||||
Log.Warn("Merge - user tried to access while another user is requesting merge",
|
s.taoLogger.Log.Warn("Merge - user tried to access while another user is requesting merge",
|
||||||
zap.String("user", r.Context().Value("user").(*models.User).Name))
|
zap.String("user", r.Context().Value("user").(*models.User).Name))
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
Log.Debug("Merge - user has access", zap.String("user", r.Context().Value("user").(*models.User).Name))
|
s.taoLogger.Log.Debug("Merge - user has access", zap.String("user", r.Context().Value("user").(*models.User).Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetMap map[string]interface{}
|
var targetMap map[string]interface{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&targetMap)
|
err := json.NewDecoder(r.Body).Decode(&targetMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
Log.Fatal("Merge request failed", zap.Error(err))
|
s.taoLogger.Log.Fatal("Merge request failed", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
repo_path := "cofffeemachineConfig/coffeethai02_"
|
repo_path := "cofffeemachineConfig/coffeethai02_"
|
||||||
|
|
@ -187,12 +221,12 @@ func (s *Server) createHandler() {
|
||||||
// find target file in the cofffeemachineConfig
|
// find target file in the cofffeemachineConfig
|
||||||
if _, err := os.Stat(repo_path + master_version + ".json"); err != nil {
|
if _, err := os.Stat(repo_path + master_version + ".json"); err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
Log.Fatal("Merge request failed. Master file not found: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Merge request failed. Master file not found: ", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(repo_path + dev_version + ".json"); err != nil {
|
if _, err := os.Stat(repo_path + dev_version + ".json"); err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
Log.Fatal("Merge request failed. Dev file not found: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Merge request failed. Dev file not found: ", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,59 +235,59 @@ func (s *Server) createHandler() {
|
||||||
|
|
||||||
// Get who's requesting
|
// Get who's requesting
|
||||||
user := r.Context().Value("user").(*models.User)
|
user := r.Context().Value("user").(*models.User)
|
||||||
Log.Info("Request merge by", zap.String("user", user.Name))
|
s.taoLogger.Log.Info("Request merge by", zap.String("user", user.Name))
|
||||||
|
|
||||||
// lookup for python exec
|
// lookup for python exec
|
||||||
py_exec, err := exec.LookPath("python")
|
py_exec, err := exec.LookPath("python")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Python error: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Python error: ", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
py_exec, err = filepath.Abs(py_exec)
|
py_exec, err = filepath.Abs(py_exec)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info("Found python exec: ", zap.String("PythonPath", py_exec))
|
s.taoLogger.Log.Info("Found python exec: ", zap.String("PythonPath", py_exec))
|
||||||
// target api file
|
// target api file
|
||||||
merge_api, api_err := os.Open("./python_api/merge_recipe.py")
|
merge_api, api_err := os.Open("./python_api/merge_recipe.py")
|
||||||
if api_err != nil {
|
if api_err != nil {
|
||||||
Log.Fatal("Merge request failed. Python api error: ", zap.String("ApiErr", api_err.Error()))
|
s.taoLogger.Log.Fatal("Merge request failed. Python api error: ", zap.String("ApiErr", api_err.Error()))
|
||||||
}
|
}
|
||||||
defer merge_api.Close()
|
defer merge_api.Close()
|
||||||
// log.Println("Locate python api", merge_api.Name())
|
// log.Println("Locate python api", merge_api.Name())
|
||||||
Log.Info("Locate python api", zap.String("ApiName", merge_api.Name()))
|
s.taoLogger.Log.Info("Locate python api", zap.String("ApiName", merge_api.Name()))
|
||||||
cmd := exec.Command(py_exec, merge_api.Name(), "merge", master_path, dev_path, output_path, changelog_path, "", user.Name)
|
cmd := exec.Command(py_exec, merge_api.Name(), "merge", master_path, dev_path, output_path, changelog_path, "", user.Name)
|
||||||
|
|
||||||
// log.Println("Run merge command", cmd)
|
// log.Println("Run merge command", cmd)
|
||||||
Log.Info("Merge", zap.String("master", master_path), zap.String("dev", dev_path), zap.String("output", output_path))
|
s.taoLogger.Log.Info("Merge", zap.String("master", master_path), zap.String("dev", dev_path), zap.String("output", output_path))
|
||||||
Log.Debug("Run merge command", zap.String("Command", cmd.String()))
|
s.taoLogger.Log.Debug("Run merge command", zap.String("Command", cmd.String()))
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
// log.Println(string(out))
|
// log.Println(string(out))
|
||||||
Log.Debug("Merge output", zap.String("Output", string(out)))
|
s.taoLogger.Log.Debug("Merge output", zap.String("Output", string(out)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// log.Fatalln("Merge request failed. Python merge failed: ", err)
|
// log.Fatalln("Merge request failed. Python merge failed: ", err)
|
||||||
Log.Fatal("Merge request failed. Python merge failed", zap.Error(err))
|
s.taoLogger.Log.Fatal("Merge request failed. Python merge failed", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(map[string]string{"message": "Merge success"})
|
json.NewEncoder(w).Encode(map[string]string{"message": "Merge success"})
|
||||||
Log.Info("Merge success", zap.String("output", "merge success"))
|
s.taoLogger.Log.Info("Merge success", zap.String("output", "merge success"))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Post("/dllog", func(w http.ResponseWriter, r *http.Request) {
|
r.Post("/dllog", func(w http.ResponseWriter, r *http.Request) {
|
||||||
Log.Debug("Request uri = ", zap.String("uri", r.RequestURI))
|
s.taoLogger.Log.Debug("Request uri = ", zap.String("uri", r.RequestURI))
|
||||||
|
|
||||||
Log.Debug("Query param = ", zap.String("query", r.URL.Query().Get("query")))
|
s.taoLogger.Log.Debug("Query param = ", zap.String("query", r.URL.Query().Get("query")))
|
||||||
// param
|
// param
|
||||||
param := r.URL.Query().Get("query")
|
param := r.URL.Query().Get("query")
|
||||||
Log.Debug("Param = ", zap.String("param", param))
|
s.taoLogger.Log.Debug("Param = ", zap.String("param", param))
|
||||||
|
|
||||||
var postRequest map[string]interface{}
|
var postRequest map[string]interface{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&postRequest)
|
err := json.NewDecoder(r.Body).Decode(&postRequest)
|
||||||
Log.Debug("Log request: ", zap.String("postRequest", fmt.Sprintf("%+v", postRequest)))
|
s.taoLogger.Log.Debug("Log request: ", zap.String("postRequest", fmt.Sprintf("%+v", postRequest)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
Log.Fatal("Decode in request failed: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Decode in request failed: ", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,25 +303,25 @@ func (s *Server) createHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log_name := postRequest["filename"].(string)
|
log_name := postRequest["filename"].(string)
|
||||||
Log.Warn("Log file name: ", zap.String("filename", log_name))
|
s.taoLogger.Log.Warn("Log file name: ", zap.String("filename", log_name))
|
||||||
|
|
||||||
if log_name == "" {
|
if log_name == "" {
|
||||||
Log.Fatal("Empty log file name")
|
s.taoLogger.Log.Fatal("Empty log file name")
|
||||||
}
|
}
|
||||||
|
|
||||||
// log.Println("Log file ext: ", file_ext)
|
// log.Println("Log file ext: ", file_ext)
|
||||||
default_changelog_path := "cofffeemachineConfig/" + param + "/"
|
default_changelog_path := "cofffeemachineConfig/" + param + "/"
|
||||||
Log.Debug("Default changelog path: ", zap.String("default_changelog_path", default_changelog_path))
|
s.taoLogger.Log.Debug("Default changelog path: ", zap.String("default_changelog_path", default_changelog_path))
|
||||||
|
|
||||||
changelog_path := default_changelog_path + log_name + file_ext
|
changelog_path := default_changelog_path + log_name + file_ext
|
||||||
Log.Debug("Changelog path: ", zap.String("changelog_path", changelog_path))
|
s.taoLogger.Log.Debug("Changelog path: ", zap.String("changelog_path", changelog_path))
|
||||||
if strings.Contains(log_name, "cofffeemachineConfig") && strings.Contains(log_name, ".json") {
|
if strings.Contains(log_name, "cofffeemachineConfig") && strings.Contains(log_name, ".json") {
|
||||||
changelog_path = log_name
|
changelog_path = log_name
|
||||||
}
|
}
|
||||||
|
|
||||||
logFile, err := os.Open(changelog_path)
|
logFile, err := os.Open(changelog_path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Log request failed: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Log request failed: ", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,20 +332,20 @@ func (s *Server) createHandler() {
|
||||||
var logFileJson map[string]interface{}
|
var logFileJson map[string]interface{}
|
||||||
err = json.NewDecoder(logFile).Decode(&logFileJson)
|
err = json.NewDecoder(logFile).Decode(&logFileJson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Error when decode log file: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Error when decode log file: ", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(logFileJson)
|
json.NewEncoder(w).Encode(logFileJson)
|
||||||
Log.Info("Log file: ", zap.String("filename", log_name))
|
s.taoLogger.Log.Info("Log file: ", zap.String("filename", log_name))
|
||||||
} else {
|
} else {
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename=logfile"+file_ext)
|
w.Header().Set("Content-Disposition", "attachment; filename=logfile"+file_ext)
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
|
||||||
_, err = io.Copy(w, logFile)
|
_, err = io.Copy(w, logFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Could not send blob", zap.Error(err))
|
s.taoLogger.Log.Fatal("Could not send blob", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -324,25 +358,25 @@ func (s *Server) createHandler() {
|
||||||
//spl
|
//spl
|
||||||
spl_path := strings.Split(r.RequestURI, "/")
|
spl_path := strings.Split(r.RequestURI, "/")
|
||||||
if len(spl_path) > 3 {
|
if len(spl_path) > 3 {
|
||||||
Log.Warn("Unexpected depth: ",
|
s.taoLogger.Log.Warn("Unexpected depth: ",
|
||||||
zap.String("path", r.RequestURI),
|
zap.String("path", r.RequestURI),
|
||||||
zap.String("depth", fmt.Sprintf("%d", len(spl_path))))
|
zap.String("depth", fmt.Sprintf("%d", len(spl_path))))
|
||||||
}
|
}
|
||||||
|
|
||||||
if spl_path[2] == "" {
|
if spl_path[2] == "" {
|
||||||
Log.Error("Empty target dir", zap.String("path", r.RequestURI))
|
s.taoLogger.Log.Error("Empty target dir", zap.String("path", r.RequestURI))
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Debug("Split path = ", zap.Any("paths", spl_path))
|
s.taoLogger.Log.Debug("Split path = ", zap.Any("paths", spl_path))
|
||||||
|
|
||||||
// Log.Info("Target dir: ", zap.String("dir", "cofffeemachineConfig"))
|
// s.taoLogger.Log.Info("Target dir: ", zap.String("dir", "cofffeemachineConfig"))
|
||||||
|
|
||||||
main_folder := "cofffeemachineConfig"
|
main_folder := "cofffeemachineConfig"
|
||||||
target_path := main_folder + "/" + spl_path[2]
|
target_path := main_folder + "/" + spl_path[2]
|
||||||
|
|
||||||
dir, err := os.ReadDir(target_path)
|
dir, err := os.ReadDir(target_path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Error("Error while trying to read dir: ", zap.String("dir", target_path), zap.Error(err))
|
s.taoLogger.Log.Error("Error while trying to read dir: ", zap.String("dir", target_path), zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,12 +390,12 @@ func (s *Server) createHandler() {
|
||||||
file_ext = ".json"
|
file_ext = ".json"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
Log.Debug("Set file ext = ", zap.String("file_ext", file_ext))
|
s.taoLogger.Log.Debug("Set file ext = ", zap.String("file_ext", file_ext))
|
||||||
|
|
||||||
displayable := make([]string, 0)
|
displayable := make([]string, 0)
|
||||||
for _, file := range dir {
|
for _, file := range dir {
|
||||||
if strings.Contains(file.Name(), file_ext) {
|
if strings.Contains(file.Name(), file_ext) {
|
||||||
Log.Debug("Found file: ", zap.String("file", file.Name()))
|
s.taoLogger.Log.Debug("Found file: ", zap.String("file", file.Name()))
|
||||||
displayable = append(displayable, file.Name()[:len(file.Name())-len(filepath.Ext(file.Name()))])
|
displayable = append(displayable, file.Name()[:len(file.Name())-len(filepath.Ext(file.Name()))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -370,7 +404,7 @@ func (s *Server) createHandler() {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(map[string][]string{"dirs": displayable})
|
json.NewEncoder(w).Encode(map[string][]string{"dirs": displayable})
|
||||||
Log.Debug("Scan dir completed < ", zap.String("path", r.RequestURI))
|
s.taoLogger.Log.Debug("Scan dir completed < ", zap.String("path", r.RequestURI))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/get_log_relation", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/get_log_relation", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -378,27 +412,27 @@ func (s *Server) createHandler() {
|
||||||
// Python looker
|
// Python looker
|
||||||
py_exec, err := exec.LookPath("python")
|
py_exec, err := exec.LookPath("python")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Error("Error while trying to find python: ", zap.Error(err))
|
s.taoLogger.Log.Error("Error while trying to find python: ", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
merge_timeline, api_err := os.Open("./python_api/merge_timeline.py")
|
merge_timeline, api_err := os.Open("./python_api/merge_timeline.py")
|
||||||
if api_err != nil {
|
if api_err != nil {
|
||||||
Log.Error("Error while trying to open merge_timeline.json: ", zap.Error(api_err))
|
s.taoLogger.Log.Error("Error while trying to open merge_timeline.json: ", zap.Error(api_err))
|
||||||
http.Error(w, api_err.Error(), http.StatusInternalServerError)
|
http.Error(w, api_err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
defer merge_timeline.Close()
|
defer merge_timeline.Close()
|
||||||
|
|
||||||
cmd := exec.Command(py_exec, merge_timeline.Name(), "get_relate")
|
cmd := exec.Command(py_exec, merge_timeline.Name(), "get_relate")
|
||||||
|
|
||||||
Log.Debug("Command: ", zap.String("command", cmd.String()))
|
s.taoLogger.Log.Debug("Command: ", zap.String("command", cmd.String()))
|
||||||
|
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
Log.Debug("Output: ", zap.String("output", string(out)))
|
s.taoLogger.Log.Debug("Output: ", zap.String("output", string(out)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Error("Error while trying to run python: ", zap.Error(err))
|
s.taoLogger.Log.Error("Error while trying to run python: ", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,28 +448,29 @@ func (s *Server) createHandler() {
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Post("/diffpy/*", func(w http.ResponseWriter, r *http.Request) {
|
r.Post("/diffpy/*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
Log.Debug("Diffpy: ", zap.String("path", r.RequestURI))
|
s.taoLogger.Log.Debug("Diffpy: ", zap.String("path", r.RequestURI))
|
||||||
// TODO: add command exec `python_Exec` `merge_recipe.py` `diff` `master_version` `version-version-version` `debug?` `flatten={true|false}` `out={true|false}`
|
// TODO: add command exec `python_Exec` `merge_recipe.py` `diff` `master_version` `version-version-version` `debug?` `flatten={true|false}` `out={true|false}`
|
||||||
})
|
})
|
||||||
|
|
||||||
sheetService, err := sheet.NewSheetService(context.Background(), s.cfg)
|
sheetService, err := sheet.NewSheetService(context.Background(), s.cfg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Error while trying to create sheet service: ", zap.Error(err))
|
s.taoLogger.Log.Fatal("Error while trying to create sheet service: ", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recipe Service
|
|
||||||
rs := recipe.NewRecipeService(database)
|
|
||||||
|
|
||||||
// Recipe Router
|
// Recipe Router
|
||||||
rr := routers.NewRecipeRouter(database, rs, sheetService)
|
rr := routers.NewRecipeRouter(s.data, recipeService, sheetService, s.taoLogger)
|
||||||
rr.Route(r)
|
rr.Route(r)
|
||||||
|
|
||||||
// Material Router
|
// Material Router
|
||||||
mr := routers.NewMaterialRouter(database)
|
mr := routers.NewMaterialRouter(s.data)
|
||||||
mr.Route(r)
|
mr.Route(r)
|
||||||
|
|
||||||
|
// User Router
|
||||||
|
ur := routers.NewUserRouter(s.taoLogger, userService)
|
||||||
|
ur.Route(r)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -454,11 +489,11 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
||||||
func pyAPIhandler(w http.ResponseWriter, r *http.Request) bool {
|
func pyAPIhandler(w http.ResponseWriter, r *http.Request) bool {
|
||||||
timeout := 10 * time.Second
|
timeout := 10 * time.Second
|
||||||
|
|
||||||
if !lockThenTimeout(&python_api_lock, timeout) {
|
if !lockThenTimeout(&pythonApiLock, timeout) {
|
||||||
http.Error(w, "API is busy", http.StatusServiceUnavailable)
|
http.Error(w, "API is busy", http.StatusServiceUnavailable)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer python_api_lock.Unlock()
|
defer pythonApiLock.Unlock()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,76 +2,66 @@ package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"recipe-manager/config"
|
||||||
|
|
||||||
"github.com/natefinch/lumberjack"
|
"github.com/natefinch/lumberjack"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type TaoLogger struct {
|
||||||
log_inst = _NewLogger()
|
cfg *config.ServerConfig
|
||||||
|
enableDebug bool
|
||||||
|
Log *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
enable_debug = false
|
func (tl *TaoLogger) initConfig() *zap.Logger {
|
||||||
log_level = zap.NewAtomicLevel()
|
|
||||||
|
|
||||||
log_file_config = zapcore.AddSync(&lumberjack.Logger{
|
enableDebugMode := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
||||||
Filename: "services/logger/serverlog.log",
|
return lvl >= zap.InfoLevel || tl.enableDebug
|
||||||
MaxSize: 500, // megabytes
|
|
||||||
MaxBackups: 3,
|
|
||||||
MaxAge: 28, //days
|
|
||||||
LocalTime: true,
|
|
||||||
})
|
|
||||||
json_enc = zapcore.NewJSONEncoder(zapcore.EncoderConfig{
|
|
||||||
TimeKey: "timestamp",
|
|
||||||
LevelKey: "level",
|
|
||||||
NameKey: "logger",
|
|
||||||
CallerKey: "caller",
|
|
||||||
MessageKey: "message",
|
|
||||||
StacktraceKey: "error",
|
|
||||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
|
||||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
|
||||||
})
|
|
||||||
console_enc = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
|
|
||||||
TimeKey: "timestamp",
|
|
||||||
LevelKey: "level",
|
|
||||||
NameKey: "logger",
|
|
||||||
CallerKey: "caller",
|
|
||||||
MessageKey: "message",
|
|
||||||
StacktraceKey: "error",
|
|
||||||
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
|
||||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
func createLogggerConfig() *zap.Logger {
|
|
||||||
|
|
||||||
enable_debug_mode := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
||||||
return lvl >= zap.InfoLevel || enable_debug
|
|
||||||
})
|
})
|
||||||
|
|
||||||
log_core := zapcore.NewTee(
|
logCore := zapcore.NewTee(
|
||||||
zapcore.NewCore(json_enc, log_file_config, enable_debug_mode),
|
zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{
|
||||||
zapcore.NewCore(console_enc, zapcore.AddSync(os.Stdout), enable_debug_mode),
|
TimeKey: "timestamp",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
CallerKey: "caller",
|
||||||
|
MessageKey: "message",
|
||||||
|
StacktraceKey: "error",
|
||||||
|
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||||
|
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||||
|
}), zapcore.AddSync(&lumberjack.Logger{
|
||||||
|
Filename: "services/logger/serverlog.log",
|
||||||
|
MaxSize: 500, // megabytes
|
||||||
|
MaxBackups: 3,
|
||||||
|
MaxAge: 28, //days
|
||||||
|
LocalTime: true,
|
||||||
|
}), enableDebugMode),
|
||||||
|
zapcore.NewCore(zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
|
||||||
|
TimeKey: "timestamp",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
CallerKey: "caller",
|
||||||
|
MessageKey: "message",
|
||||||
|
StacktraceKey: "error",
|
||||||
|
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||||
|
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||||
|
}), zapcore.AddSync(os.Stdout), enableDebugMode),
|
||||||
)
|
)
|
||||||
|
|
||||||
return zap.New(log_core)
|
return zap.New(logCore)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _NewLogger() *zap.Logger {
|
func NewTaoLogger(cfg *config.ServerConfig) *TaoLogger {
|
||||||
log := createLogggerConfig()
|
logger := &TaoLogger{cfg, false, nil}
|
||||||
defer log.Sync()
|
logger.Log = logger.initConfig()
|
||||||
return log
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetInstance() *zap.Logger {
|
if cfg.Debug {
|
||||||
return log_inst
|
// logger.SetLevel("DEBUG")
|
||||||
}
|
logger.Log.Debug("Debug mode", zap.Bool("enable", cfg.Debug))
|
||||||
|
logger.enableDebug = true
|
||||||
|
}
|
||||||
|
|
||||||
func EnableDebug(state bool) {
|
return logger
|
||||||
enable_debug = state
|
|
||||||
log_inst.Debug("EnableDebug", zap.Bool("enable", state))
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDbgState() bool {
|
|
||||||
return enable_debug
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,9 @@ func (o *oauthService) GetUserInfo(ctx context.Context, token *oauth2.Token) (*m
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var userInfo map[string]interface{}
|
var userInfo map[string]interface{}
|
||||||
json.NewDecoder(resp.Body).Decode(&userInfo)
|
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if userInfo["error"] != nil {
|
if userInfo["error"] != nil {
|
||||||
return nil, errors.New("Error getting user info")
|
return nil, errors.New("Error getting user info")
|
||||||
|
|
|
||||||
9
server/services/user/queries/query.go
Normal file
9
server/services/user/queries/query.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
package queries
|
||||||
|
|
||||||
|
const (
|
||||||
|
GetUserByID = "SELECT id, name, email, picture, permissions FROM users WHERE id = ?"
|
||||||
|
GetUserByEmail = "SELECT id, name, email, picture, permissions FROM users WHERE email = ?"
|
||||||
|
CreateUser = "INSERT INTO users (id, name, email, picture, permissions) VALUES (? , ?, ?, ? , ?)"
|
||||||
|
SetNameUser = "UPDATE users SET name = ? WHERE id = ?"
|
||||||
|
SetPermissionsUser = "UPDATE users SET permissions = ? WHERE id = ?"
|
||||||
|
)
|
||||||
112
server/services/user/user.go
Normal file
112
server/services/user/user.go
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"recipe-manager/config"
|
||||||
|
"recipe-manager/enums/permissions"
|
||||||
|
"recipe-manager/models"
|
||||||
|
"recipe-manager/services/logger"
|
||||||
|
"recipe-manager/services/user/queries"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService interface {
|
||||||
|
CreateNewUser(ctx context.Context, name, email, picture string, permissions permissions.Permission) error
|
||||||
|
UpdateName(ctx context.Context, userID, name string) error
|
||||||
|
UpdatePermissions(ctx context.Context, userID string, permissions permissions.Permission) error
|
||||||
|
|
||||||
|
GetUserByID(ctx context.Context, userID string) (*models.User, error)
|
||||||
|
GetUserByEmail(ctx context.Context, email string) (*models.User, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type userService struct {
|
||||||
|
cft *config.ServerConfig
|
||||||
|
db *sqlx.DB
|
||||||
|
taoLogger *logger.TaoLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) CreateNewUser(ctx context.Context, name, email, picture string, permissions permissions.Permission) error {
|
||||||
|
|
||||||
|
user := ctx.Value("user").(*models.User)
|
||||||
|
u.taoLogger.Log.Info("User.CreateNewUser", zap.Reflect("user", map[string]interface{}{
|
||||||
|
"name": name,
|
||||||
|
"email": email,
|
||||||
|
"picture": picture,
|
||||||
|
"permissions": permissions,
|
||||||
|
}), zap.String("by", user.Email))
|
||||||
|
|
||||||
|
userID := uuid.New()
|
||||||
|
|
||||||
|
_, err := u.db.ExecContext(ctx, queries.CreateUser, userID, name, email, picture, permissions)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
u.taoLogger.Log.Error("User.CreateNewUser", zap.Error(err), zap.String("by", user.Email))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) UpdateName(ctx context.Context, userID, name string) error {
|
||||||
|
|
||||||
|
user := ctx.Value("user").(*models.User)
|
||||||
|
u.taoLogger.Log.Info("User.UpdateName", zap.String("userID", userID), zap.String("name", name), zap.String("by", user.Email))
|
||||||
|
|
||||||
|
_, err := u.db.ExecContext(ctx, queries.SetNameUser, name, userID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
u.taoLogger.Log.Error("User.UpdateName", zap.Error(err), zap.String("by", user.Email))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) UpdatePermissions(ctx context.Context, userID string, perms permissions.Permission) error {
|
||||||
|
|
||||||
|
user := ctx.Value("user").(*models.User)
|
||||||
|
u.taoLogger.Log.Info("User.UpdatePermissions", zap.String("userID", userID), zap.Uint("permissions", uint(perms)), zap.String("by", user.Email))
|
||||||
|
|
||||||
|
_, err := u.db.ExecContext(ctx, queries.SetPermissionsUser, perms, userID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
u.taoLogger.Log.Error("User.UpdatePermissions", zap.Error(err), zap.String("by", user.Email))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) GetUserByID(ctx context.Context, userID string) (*models.User, error) {
|
||||||
|
|
||||||
|
userResult := &models.User{}
|
||||||
|
if err := u.db.GetContext(ctx, userResult, queries.GetUserByID, userID); err != nil {
|
||||||
|
u.taoLogger.Log.Error("User.GetUserByID", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return userResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
|
|
||||||
|
userResult := &models.User{}
|
||||||
|
if err := u.db.GetContext(ctx, userResult, queries.GetUserByEmail, email); err != nil {
|
||||||
|
u.taoLogger.Log.Error("User.GetUserByEmail", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return userResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserService(cfg *config.ServerConfig, db *sqlx.DB, taoLogger *logger.TaoLogger) UserService {
|
||||||
|
return &userService{cfg, db, taoLogger}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue