mirror of
https://github.com/usememos/memos.git
synced 2025-04-13 00:52:07 +02:00
feat: update finding memo with visibility
This commit is contained in:
parent
58e68f8f80
commit
b7339e00ba
@ -1,5 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
|
var (
|
||||||
|
UNKNOWN_ID = 0
|
||||||
|
)
|
||||||
|
|
||||||
type Signin struct {
|
type Signin struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
10
api/memo.go
10
api/memo.go
@ -6,6 +6,8 @@ type Visibility string
|
|||||||
const (
|
const (
|
||||||
// Public is the PUBLIC visibility.
|
// Public is the PUBLIC visibility.
|
||||||
Public Visibility = "PUBLIC"
|
Public Visibility = "PUBLIC"
|
||||||
|
// Protected is the PROTECTED visibility.
|
||||||
|
Protected Visibility = "PROTECTED"
|
||||||
// Privite is the PRIVATE visibility.
|
// Privite is the PRIVATE visibility.
|
||||||
Privite Visibility = "PRIVATE"
|
Privite Visibility = "PRIVATE"
|
||||||
)
|
)
|
||||||
@ -14,6 +16,8 @@ func (e Visibility) String() string {
|
|||||||
switch e {
|
switch e {
|
||||||
case Public:
|
case Public:
|
||||||
return "PUBLIC"
|
return "PUBLIC"
|
||||||
|
case Protected:
|
||||||
|
return "PROTECTED"
|
||||||
case Privite:
|
case Privite:
|
||||||
return "PRIVATE"
|
return "PRIVATE"
|
||||||
}
|
}
|
||||||
@ -65,9 +69,9 @@ type MemoFind struct {
|
|||||||
CreatorID *int `json:"creatorId"`
|
CreatorID *int `json:"creatorId"`
|
||||||
|
|
||||||
// Domain specific fields
|
// Domain specific fields
|
||||||
Pinned *bool
|
Pinned *bool
|
||||||
ContentSearch *string
|
ContentSearch *string
|
||||||
Visibility *Visibility
|
VisibilityList []Visibility
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
Limit int
|
Limit int
|
||||||
|
@ -21,30 +21,30 @@ func getUserIDContextKey() string {
|
|||||||
return userIDContextKey
|
return userIDContextKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUserSession(c echo.Context, user *api.User) error {
|
func setUserSession(ctx echo.Context, user *api.User) error {
|
||||||
sess, _ := session.Get("session", c)
|
sess, _ := session.Get("session", ctx)
|
||||||
sess.Options = &sessions.Options{
|
sess.Options = &sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: 1000 * 3600 * 24 * 30,
|
MaxAge: 1000 * 3600 * 24 * 30,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
}
|
}
|
||||||
sess.Values[userIDContextKey] = user.ID
|
sess.Values[userIDContextKey] = user.ID
|
||||||
err := sess.Save(c.Request(), c.Response())
|
err := sess.Save(ctx.Request(), ctx.Response())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set session, err: %w", err)
|
return fmt.Errorf("failed to set session, err: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeUserSession(c echo.Context) error {
|
func removeUserSession(ctx echo.Context) error {
|
||||||
sess, _ := session.Get("session", c)
|
sess, _ := session.Get("session", ctx)
|
||||||
sess.Options = &sessions.Options{
|
sess.Options = &sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: 0,
|
MaxAge: 0,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
}
|
}
|
||||||
sess.Values[userIDContextKey] = nil
|
sess.Values[userIDContextKey] = nil
|
||||||
err := sess.Save(c.Request(), c.Response())
|
err := sess.Save(ctx.Request(), ctx.Response())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set session, err: %w", err)
|
return fmt.Errorf("failed to set session, err: %w", err)
|
||||||
}
|
}
|
||||||
@ -53,14 +53,14 @@ func removeUserSession(c echo.Context) error {
|
|||||||
|
|
||||||
// Use session to store user.id.
|
// Use session to store user.id.
|
||||||
func BasicAuthMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc {
|
func BasicAuthMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(ctx echo.Context) error {
|
||||||
// Skip auth for some paths.
|
// Skip auth for some paths.
|
||||||
if common.HasPrefixes(c.Path(), "/api/auth", "/api/ping", "/api/status", "/api/user/:userId") {
|
if common.HasPrefixes(ctx.Path(), "/api/auth", "/api/ping", "/api/status", "/api/user/:userId") {
|
||||||
return next(c)
|
return next(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is openId in query string and related user is found, then skip auth.
|
// If there is openId in query string and related user is found, then skip auth.
|
||||||
openID := c.QueryParam("openId")
|
openID := ctx.QueryParam("openId")
|
||||||
if openID != "" {
|
if openID != "" {
|
||||||
userFind := &api.UserFind{
|
userFind := &api.UserFind{
|
||||||
OpenID: &openID,
|
OpenID: &openID,
|
||||||
@ -71,49 +71,49 @@ func BasicAuthMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
}
|
}
|
||||||
if user != nil {
|
if user != nil {
|
||||||
// Stores userID into context.
|
// Stores userID into context.
|
||||||
c.Set(getUserIDContextKey(), user.ID)
|
ctx.Set(getUserIDContextKey(), user.ID)
|
||||||
return next(c)
|
return next(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if common.HasPrefixes(c.Path(), "/api/memo", "/api/tag", "/api/shortcut") && c.Request().Method == http.MethodGet {
|
needAuth := true
|
||||||
if _, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
|
if common.HasPrefixes(ctx.Path(), "/api/memo", "/api/tag", "/api/shortcut") && ctx.Request().Method == http.MethodGet {
|
||||||
return next(c)
|
if _, err := strconv.Atoi(ctx.QueryParam("creatorId")); err == nil {
|
||||||
|
needAuth = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess, err := session.Get("session", c)
|
{
|
||||||
if err != nil {
|
sess, _ := session.Get("session", ctx)
|
||||||
return echo.NewHTTPError(http.StatusUnauthorized, "Missing session").SetInternal(err)
|
userIDValue := sess.Values[userIDContextKey]
|
||||||
|
if userIDValue == nil && needAuth {
|
||||||
|
return echo.NewHTTPError(http.StatusUnauthorized, "Missing userID in session")
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, err := strconv.Atoi(fmt.Sprintf("%v", userIDValue))
|
||||||
|
if err != nil && needAuth {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to malformatted user id in the session.").SetInternal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userFind := &api.UserFind{
|
||||||
|
ID: &userID,
|
||||||
|
}
|
||||||
|
user, err := s.Store.FindUser(userFind)
|
||||||
|
if err != nil && needAuth {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by ID: %d", userID)).SetInternal(err)
|
||||||
|
}
|
||||||
|
if needAuth {
|
||||||
|
if user == nil {
|
||||||
|
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("Not found user ID: %d", userID))
|
||||||
|
} else if user.RowStatus == api.Archived {
|
||||||
|
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with email %s", user.Email))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save userID into context.
|
||||||
|
ctx.Set(getUserIDContextKey(), userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
userIDValue := sess.Values[userIDContextKey]
|
return next(ctx)
|
||||||
if userIDValue == nil {
|
|
||||||
return echo.NewHTTPError(http.StatusUnauthorized, "Missing userID in session")
|
|
||||||
}
|
|
||||||
|
|
||||||
userID, err := strconv.Atoi(fmt.Sprintf("%v", userIDValue))
|
|
||||||
if err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to malformatted user id in the session.").SetInternal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Even if there is no error, we still need to make sure the user still exists.
|
|
||||||
userFind := &api.UserFind{
|
|
||||||
ID: &userID,
|
|
||||||
}
|
|
||||||
user, err := s.Store.FindUser(userFind)
|
|
||||||
if err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by ID: %d", userID)).SetInternal(err)
|
|
||||||
}
|
|
||||||
if user == nil {
|
|
||||||
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("Not found user ID: %d", userID))
|
|
||||||
} else if user.RowStatus == api.Archived {
|
|
||||||
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with email %s", user.Email))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stores userID into context.
|
|
||||||
c.Set(getUserIDContextKey(), userID)
|
|
||||||
|
|
||||||
return next(c)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/usememos/memos/api"
|
"github.com/usememos/memos/api"
|
||||||
"github.com/usememos/memos/common"
|
"github.com/usememos/memos/common"
|
||||||
@ -68,21 +69,21 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
|||||||
memoFind := &api.MemoFind{}
|
memoFind := &api.MemoFind{}
|
||||||
|
|
||||||
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
|
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
|
||||||
memoFind.CreatorID = &userID
|
|
||||||
} else {
|
|
||||||
userID, ok := c.Get(getUserIDContextKey()).(int)
|
|
||||||
if !ok {
|
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo")
|
|
||||||
}
|
|
||||||
|
|
||||||
memoFind.CreatorID = &userID
|
memoFind.CreatorID = &userID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only can get PUBLIC memos in visitor mode
|
currentUserID := c.Get(getUserIDContextKey()).(int)
|
||||||
_, ok := c.Get(getUserIDContextKey()).(int)
|
if currentUserID == api.UNKNOWN_ID {
|
||||||
if !ok {
|
if memoFind.CreatorID == nil {
|
||||||
publicVisibility := api.Public
|
return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo")
|
||||||
memoFind.Visibility = &publicVisibility
|
}
|
||||||
|
memoFind.VisibilityList = []api.Visibility{api.Public}
|
||||||
|
} else {
|
||||||
|
if memoFind.CreatorID == nil {
|
||||||
|
memoFind.CreatorID = ¤tUserID
|
||||||
|
} else {
|
||||||
|
memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rowStatus := api.RowStatus(c.QueryParam("rowStatus"))
|
rowStatus := api.RowStatus(c.QueryParam("rowStatus"))
|
||||||
@ -99,6 +100,14 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
|||||||
contentSearch := "#" + tag + " "
|
contentSearch := "#" + tag + " "
|
||||||
memoFind.ContentSearch = &contentSearch
|
memoFind.ContentSearch = &contentSearch
|
||||||
}
|
}
|
||||||
|
visibilitListStr := c.QueryParam("visibility")
|
||||||
|
if visibilitListStr != "" {
|
||||||
|
visibilityList := []api.Visibility{}
|
||||||
|
for _, visibility := range strings.Split(visibilitListStr, ",") {
|
||||||
|
visibilityList = append(visibilityList, api.Visibility(visibility))
|
||||||
|
}
|
||||||
|
memoFind.VisibilityList = visibilityList
|
||||||
|
}
|
||||||
if limit, err := strconv.Atoi(c.QueryParam("limit")); err == nil {
|
if limit, err := strconv.Atoi(c.QueryParam("limit")); err == nil {
|
||||||
memoFind.Limit = limit
|
memoFind.Limit = limit
|
||||||
}
|
}
|
||||||
@ -190,9 +199,7 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
|||||||
memoDelete := &api.MemoDelete{
|
memoDelete := &api.MemoDelete{
|
||||||
ID: memoID,
|
ID: memoID,
|
||||||
}
|
}
|
||||||
|
if err := s.Store.DeleteMemo(memoDelete); err != nil {
|
||||||
err = s.Store.DeleteMemo(memoDelete)
|
|
||||||
if err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to delete memo ID: %v", memoID)).SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to delete memo ID: %v", memoID)).SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,21 +22,21 @@ func (s *Server) registerTagRoutes(g *echo.Group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
|
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
|
||||||
memoFind.CreatorID = &userID
|
|
||||||
} else {
|
|
||||||
userID, ok := c.Get(getUserIDContextKey()).(int)
|
|
||||||
if !ok {
|
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find tag")
|
|
||||||
}
|
|
||||||
|
|
||||||
memoFind.CreatorID = &userID
|
memoFind.CreatorID = &userID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only can get PUBLIC memos in visitor mode
|
currentUserID := c.Get(getUserIDContextKey()).(int)
|
||||||
_, ok := c.Get(getUserIDContextKey()).(int)
|
if currentUserID == api.UNKNOWN_ID {
|
||||||
if !ok {
|
if memoFind.CreatorID == nil {
|
||||||
publicVisibility := api.Public
|
return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo")
|
||||||
memoFind.Visibility = &publicVisibility
|
}
|
||||||
|
memoFind.VisibilityList = []api.Visibility{api.Public}
|
||||||
|
} else {
|
||||||
|
if memoFind.CreatorID == nil {
|
||||||
|
memoFind.CreatorID = ¤tUserID
|
||||||
|
} else {
|
||||||
|
memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memoList, err := s.Store.FindMemoList(&memoFind)
|
memoList, err := s.Store.FindMemoList(&memoFind)
|
||||||
|
@ -182,4 +182,34 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
g.DELETE("/user/:userId", func(c echo.Context) error {
|
||||||
|
currentUserID := c.Get(getUserIDContextKey()).(int)
|
||||||
|
currentUser, err := s.Store.FindUser(&api.UserFind{
|
||||||
|
ID: ¤tUserID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err)
|
||||||
|
}
|
||||||
|
if currentUser == nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Current session user not found with ID: %d", currentUserID)).SetInternal(err)
|
||||||
|
} else if currentUser.Role != api.Host {
|
||||||
|
return echo.NewHTTPError(http.StatusForbidden, "Access forbidden for current session user").SetInternal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, err := strconv.Atoi(c.Param("userId"))
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("userId"))).SetInternal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userDelete := &api.UserDelete{
|
||||||
|
ID: userID,
|
||||||
|
}
|
||||||
|
if err := s.Store.DeleteUser(userDelete); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete user").SetInternal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, true)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@ INSERT INTO
|
|||||||
memo (
|
memo (
|
||||||
`id`,
|
`id`,
|
||||||
`content`,
|
`content`,
|
||||||
`creator_id`
|
`creator_id`,
|
||||||
|
`visibility`
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
@ -26,7 +27,8 @@ VALUES
|
|||||||
- [x] Clean the room;
|
- [x] Clean the room;
|
||||||
- [x] Read *📖 The Little Prince*;
|
- [x] Read *📖 The Little Prince*;
|
||||||
(👆 click to toggle status)',
|
(👆 click to toggle status)',
|
||||||
101
|
101,
|
||||||
|
'PROTECTED'
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
@ -48,7 +50,8 @@ INSERT INTO
|
|||||||
memo (
|
memo (
|
||||||
`id`,
|
`id`,
|
||||||
`content`,
|
`content`,
|
||||||
`creator_id`
|
`creator_id`,
|
||||||
|
`visibility`
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
@ -59,7 +62,8 @@ VALUES
|
|||||||
- [ ] Watch *👦 The Boys*;
|
- [ ] Watch *👦 The Boys*;
|
||||||
(👆 click to toggle status)
|
(👆 click to toggle status)
|
||||||
',
|
',
|
||||||
102
|
102,
|
||||||
|
'PROTECTED'
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
|
@ -222,8 +222,13 @@ func findMemoRawList(db *sql.DB, find *api.MemoFind) ([]*memoRaw, error) {
|
|||||||
if v := find.ContentSearch; v != nil {
|
if v := find.ContentSearch; v != nil {
|
||||||
where, args = append(where, "content LIKE ?"), append(args, "%"+*v+"%")
|
where, args = append(where, "content LIKE ?"), append(args, "%"+*v+"%")
|
||||||
}
|
}
|
||||||
if v := find.Visibility; v != nil {
|
if v := find.VisibilityList; len(v) != 0 {
|
||||||
where, args = append(where, "visibility = ?"), append(args, *v)
|
list := []string{}
|
||||||
|
for _, visibility := range v {
|
||||||
|
list = append(list, fmt.Sprintf("$%d", len(args)+1))
|
||||||
|
args = append(args, visibility)
|
||||||
|
}
|
||||||
|
where = append(where, fmt.Sprintf("visibility in (%s)", strings.Join(list, ",")))
|
||||||
}
|
}
|
||||||
|
|
||||||
pagination := ""
|
pagination := ""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user