diff --git a/server/db/crud.go b/server/db/crud.go index 7613550..375d4d8 100644 --- a/server/db/crud.go +++ b/server/db/crud.go @@ -13,7 +13,7 @@ import ( func Create(i DbItem) error { - i.buildKeys() + i.BuildKeys() av, err := attributevalue.MarshalMap(i) if err != nil { @@ -32,11 +32,11 @@ func Create(i DbItem) error { // out must be a non-nil pointer func Fetch(i DbItem, out interface{}) error { - i.buildKeys() + i.BuildKeys() o, err := handle.client.GetItem(context.TODO(), &dynamodb.GetItemInput{ TableName: aws.String(handle.table), - Key: i.getKey(), + Key: i.GetKeys(), }) if err != nil { return err @@ -54,13 +54,13 @@ func Fetch(i DbItem, out interface{}) error { func FetchByGsi(i DbItem, out interface{}) error { - i.buildKeys() + i.BuildKeys() o, err := handle.client.Query(context.TODO(), &dynamodb.QueryInput{ TableName: aws.String(handle.table), IndexName: aws.String(handle.gsiName), KeyConditionExpression: aws.String(fmt.Sprintf("%s = :%s", handle.gsiAttr, handle.gsiAttr)), - ExpressionAttributeValues: i.getGsi(), + ExpressionAttributeValues: i.GetGsi(), }) if err != nil { return err @@ -85,7 +85,7 @@ func Update(i DbItem, key string, value interface{}) error { } _, err = handle.client.UpdateItem(context.TODO(), &dynamodb.UpdateItemInput{ TableName: aws.String(handle.table), - Key: i.getKey(), + Key: i.GetKeys(), UpdateExpression: aws.String(fmt.Sprintf("set %s = :val", key)), ExpressionAttributeValues: map[string]types.AttributeValue{ ":val": val, @@ -104,7 +104,7 @@ func MultiUpdate(i DbItem, keys []string, values []interface{}) error { func Delete(i DbItem) error { - k := i.getKey() + k := i.GetKeys() _, err := handle.client.DeleteItem(context.TODO(), &dynamodb.DeleteItemInput{ TableName: aws.String(handle.table), diff --git a/server/db/dbItem.go b/server/db/dbItem.go index e010be9..bcce37e 100644 --- a/server/db/dbItem.go +++ b/server/db/dbItem.go @@ -5,7 +5,7 @@ import ( ) type DbItem interface { - buildKeys() - getKey() map[string]types.AttributeValue - getGsi() map[string]types.AttributeValue + BuildKeys() + GetKeys() map[string]types.AttributeValue + GetGsi() map[string]types.AttributeValue } diff --git a/server/db/schema.go b/server/db/schema.go index a6fc13d..8fd19b2 100644 --- a/server/db/schema.go +++ b/server/db/schema.go @@ -14,20 +14,20 @@ type UserSchema struct { LastLoginAt int64 `dynamodbav:"last_login_at"` } -func (u *UserSchema) buildKeys() { +func (u *UserSchema) BuildKeys() { u.Pk = "user#" + u.Username u.Gsi1pk = "session_key#" + u.Session } -func (u *UserSchema) getKey() map[string]types.AttributeValue { - u.buildKeys() +func (u *UserSchema) GetKeys() map[string]types.AttributeValue { + u.BuildKeys() k := make(map[string]types.AttributeValue) k["pk"] = &types.AttributeValueMemberS{Value: u.Pk} return k } -func (u *UserSchema) getGsi() map[string]types.AttributeValue { - u.buildKeys() +func (u *UserSchema) GetGsi() map[string]types.AttributeValue { + u.BuildKeys() k := make(map[string]types.AttributeValue) k[":gsi1pk"] = &types.AttributeValueMemberS{Value: u.Gsi1pk} return k diff --git a/server/list/delete.go b/server/list/delete.go new file mode 100644 index 0000000..22b7714 --- /dev/null +++ b/server/list/delete.go @@ -0,0 +1,55 @@ +package list + +import ( + "net/http" + + "github.com/mnrva-dev/owltier.com/server/auth" + "github.com/mnrva-dev/owltier.com/server/db" + "github.com/mnrva-dev/owltier.com/server/jsend" +) + +func DeleteList(w http.ResponseWriter, r *http.Request) { + // get user from session key + sessC, err := r.Cookie(auth.SESSION_COOKIE) + if err != nil { + jsend.Fail(w, 401, map[string]interface{}{ + "session": "invalid session", + }) + return + } + session := sessC.Value + var user = &db.UserSchema{} + err = db.FetchByGsi(&db.UserSchema{ + Session: session, + }, user) + if err != nil { + jsend.Fail(w, 401, map[string]interface{}{ + "session": "invalid session", + }) + return + } + // get list id + r.ParseForm() + listId := r.FormValue("id") + list := &List{Id: listId} + // make sure user owns list + err = db.Fetch(list, list) + if err != nil { + jsend.Fail(w, 404, map[string]interface{}{ + "id": "list not found", + }) + return + } + if list.CreatedBy != user.Username { + jsend.Fail(w, 401, map[string]interface{}{ + "username": "user does not own this list", + }) + return + } + // delete list + db.Delete(list) + + jsend.Success(w, map[string]interface{}{ + "id": list.Id, + }) +} diff --git a/server/list/get.go b/server/list/get.go new file mode 100644 index 0000000..db18218 --- /dev/null +++ b/server/list/get.go @@ -0,0 +1,28 @@ +package list + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/mnrva-dev/owltier.com/server/db" + "github.com/mnrva-dev/owltier.com/server/jsend" +) + +func GetList(w http.ResponseWriter, r *http.Request) { + // get iist from db + listId := chi.URLParam(r, "id") + list := &List{} + list.Id = listId + err := db.Fetch(list, list) + if err != nil { + jsend.Fail(w, 404, map[string]interface{}{ + "id": "list not found", + }) + return + } + + // return list + jsend.Success(w, map[string]interface{}{ + "list": list, + }) +} diff --git a/server/list/new.go b/server/list/new.go new file mode 100644 index 0000000..0545f7d --- /dev/null +++ b/server/list/new.go @@ -0,0 +1,62 @@ +package list + +import ( + "encoding/json" + "math/rand" + "net/http" + "time" + + "github.com/mnrva-dev/owltier.com/server/auth" + "github.com/mnrva-dev/owltier.com/server/db" + "github.com/mnrva-dev/owltier.com/server/jsend" +) + +const ( + ID_LENGTH = 10 +) + +func NewList(w http.ResponseWriter, r *http.Request) { + + // get user from session key + sessC, err := r.Cookie(auth.SESSION_COOKIE) + if err != nil { + jsend.Fail(w, 401, map[string]interface{}{ + "session": "invalid session", + }) + return + } + session := sessC.Value + var user = &db.UserSchema{} + err = db.FetchByGsi(&db.UserSchema{ + Session: session, + }, user) + if err != nil { + jsend.Fail(w, 401, map[string]interface{}{ + "session": "invalid session", + }) + return + } + + // create list object from user and json data + list := &List{} + json.NewDecoder(r.Body).Decode(list) + list.CreatedAt = time.Now().Unix() + list.CreatedBy = user.Username + list.Id = makeId(ID_LENGTH) + + // create list in db + db.Create(list) + + jsend.Success(w, map[string]interface{}{ + "slug": "/list/" + list.Id, + }) +} + +func makeId(n int) string { + var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} diff --git a/server/list/router.go b/server/list/router.go new file mode 100644 index 0000000..ddfcd00 --- /dev/null +++ b/server/list/router.go @@ -0,0 +1,13 @@ +package list + +import "github.com/go-chi/chi/v5" + +func BuildRouter() *chi.Mux { + r := chi.NewRouter() + + r.Post("/new", NewList) + r.Post("/delete", DeleteList) // should this even exist? + r.Get("/{id}", GetList) + + return r +} diff --git a/server/list/schema.go b/server/list/schema.go index 87ff795..5ae4f15 100644 --- a/server/list/schema.go +++ b/server/list/schema.go @@ -3,28 +3,28 @@ package list import "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" type List struct { - Pk string `dynamodbav:"pk"` - Id string `json:"id"` - CreatedAt int64 `json:"created_at"` - CreatedBy string `json:"created_by"` - Format string `json:"format"` - Breaks []bool `json:"breaks"` - NA []string `json:"na"` - APAC []string `json:"apac"` - Combined []string `json:"combined"` + Pk string `json:"-" dynamodbav:"pk"` + Id string `json:"id" dynamodbav:"id"` + CreatedAt int64 `json:"created_at" dynamodbav:"created_at"` + CreatedBy string `json:"created_by" dynamodbav:"created_by"` + Format string `json:"format" dynamodbav:"format"` + Breaks []bool `json:"breaks" dynamodbav:"breaks"` + NA []string `json:"na,omitempty" dynamodbav:"na,omitempty"` + APAC []string `json:"apac,omitempty" dynamodbav:"apac,omitempty"` + Combined []string `json:"combined,omitempty" dynamodbav:"combined,omitempty"` } -func (u *List) buildKeys() { +func (u *List) BuildKeys() { u.Pk = "list#" + u.Id } -func (u *List) getKey() map[string]types.AttributeValue { - u.buildKeys() +func (u *List) GetKeys() map[string]types.AttributeValue { + u.BuildKeys() k := make(map[string]types.AttributeValue) k["pk"] = &types.AttributeValueMemberS{Value: u.Pk} return k } -func (u *List) getGsi() map[string]types.AttributeValue { +func (u *List) GetGsi() map[string]types.AttributeValue { return nil } diff --git a/server/run.go b/server/run.go index 075334e..29b68df 100644 --- a/server/run.go +++ b/server/run.go @@ -13,6 +13,7 @@ import ( "github.com/go-chi/chi/v5/middleware" "github.com/mnrva-dev/owltier.com/server/auth" "github.com/mnrva-dev/owltier.com/server/config" + "github.com/mnrva-dev/owltier.com/server/list" ) // boilerplate taken from go-chi on GitHub @@ -75,6 +76,7 @@ func handler() http.Handler { r.Use(middleware.Recoverer) r.Mount("/auth", auth.BuildRouter()) + r.Mount("/list", list.BuildRouter()) return r }