123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- package v1
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "strconv"
- "time"
- "github.com/labstack/echo/v4"
- "github.com/pkg/errors"
- "github.com/usememos/memos/store"
- )
- type Shortcut struct {
- ID int `json:"id"`
- // Standard fields
- RowStatus RowStatus `json:"rowStatus"`
- CreatorID int `json:"creatorId"`
- CreatedTs int64 `json:"createdTs"`
- UpdatedTs int64 `json:"updatedTs"`
- // Domain specific fields
- Title string `json:"title"`
- Payload string `json:"payload"`
- }
- type CreateShortcutRequest struct {
- Title string `json:"title"`
- Payload string `json:"payload"`
- }
- type UpdateShortcutRequest struct {
- RowStatus *RowStatus `json:"rowStatus"`
- Title *string `json:"title"`
- Payload *string `json:"payload"`
- }
- type ShortcutFind struct {
- ID *int
- // Standard fields
- CreatorID *int
- // Domain specific fields
- Title *string `json:"title"`
- }
- type ShortcutDelete struct {
- ID *int
- // Standard fields
- CreatorID *int
- }
- func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) {
- g.POST("/shortcut", func(c echo.Context) error {
- ctx := c.Request().Context()
- userID, ok := c.Get(getUserIDContextKey()).(int)
- if !ok {
- return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
- }
- shortcutCreate := &CreateShortcutRequest{}
- if err := json.NewDecoder(c.Request().Body).Decode(shortcutCreate); err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post shortcut request").SetInternal(err)
- }
- shortcut, err := s.Store.CreateShortcut(ctx, &store.Shortcut{
- CreatorID: userID,
- Title: shortcutCreate.Title,
- Payload: shortcutCreate.Payload,
- })
- if err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create shortcut").SetInternal(err)
- }
- shortcutMessage := convertShortcutFromStore(shortcut)
- if err := s.createShortcutCreateActivity(c, shortcutMessage); err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err)
- }
- return c.JSON(http.StatusOK, shortcutMessage)
- })
- g.PATCH("/shortcut/:shortcutId", func(c echo.Context) error {
- ctx := c.Request().Context()
- userID, ok := c.Get(getUserIDContextKey()).(int)
- if !ok {
- return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
- }
- shortcutID, err := strconv.Atoi(c.Param("shortcutId"))
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err)
- }
- shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
- ID: &shortcutID,
- })
- if err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find shortcut").SetInternal(err)
- }
- if shortcut == nil {
- return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Shortcut not found: %d", shortcutID))
- }
- if shortcut.CreatorID != userID {
- return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
- }
- request := &UpdateShortcutRequest{}
- if err := json.NewDecoder(c.Request().Body).Decode(request); err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch shortcut request").SetInternal(err)
- }
- currentTs := time.Now().Unix()
- shortcutUpdate := &store.UpdateShortcut{
- ID: shortcutID,
- UpdatedTs: ¤tTs,
- }
- if request.RowStatus != nil {
- rowStatus := store.RowStatus(*request.RowStatus)
- shortcutUpdate.RowStatus = &rowStatus
- }
- if request.Title != nil {
- shortcutUpdate.Title = request.Title
- }
- if request.Payload != nil {
- shortcutUpdate.Payload = request.Payload
- }
- shortcut, err = s.Store.UpdateShortcut(ctx, shortcutUpdate)
- if err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch shortcut").SetInternal(err)
- }
- return c.JSON(http.StatusOK, convertShortcutFromStore(shortcut))
- })
- g.GET("/shortcut", func(c echo.Context) error {
- ctx := c.Request().Context()
- userID, ok := c.Get(getUserIDContextKey()).(int)
- if !ok {
- return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find shortcut")
- }
- list, err := s.Store.ListShortcuts(ctx, &store.FindShortcut{
- CreatorID: &userID,
- })
- if err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get shortcut list").SetInternal(err)
- }
- shortcutMessageList := make([]*Shortcut, 0, len(list))
- for _, shortcut := range list {
- shortcutMessageList = append(shortcutMessageList, convertShortcutFromStore(shortcut))
- }
- return c.JSON(http.StatusOK, shortcutMessageList)
- })
- g.GET("/shortcut/:shortcutId", func(c echo.Context) error {
- ctx := c.Request().Context()
- shortcutID, err := strconv.Atoi(c.Param("shortcutId"))
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err)
- }
- shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
- ID: &shortcutID,
- })
- if err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to fetch shortcut by ID %d", shortcutID)).SetInternal(err)
- }
- if shortcut == nil {
- return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Shortcut not found: %d", shortcutID))
- }
- return c.JSON(http.StatusOK, convertShortcutFromStore(shortcut))
- })
- g.DELETE("/shortcut/:shortcutId", func(c echo.Context) error {
- ctx := c.Request().Context()
- userID, ok := c.Get(getUserIDContextKey()).(int)
- if !ok {
- return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
- }
- shortcutID, err := strconv.Atoi(c.Param("shortcutId"))
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err)
- }
- shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
- ID: &shortcutID,
- })
- if err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find shortcut").SetInternal(err)
- }
- if shortcut == nil {
- return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Shortcut not found: %d", shortcutID))
- }
- if shortcut.CreatorID != userID {
- return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
- }
- if err := s.Store.DeleteShortcut(ctx, &store.DeleteShortcut{
- ID: &shortcutID,
- }); err != nil {
- return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete shortcut").SetInternal(err)
- }
- return c.JSON(http.StatusOK, true)
- })
- }
- func (s *APIV1Service) createShortcutCreateActivity(c echo.Context, shortcut *Shortcut) error {
- ctx := c.Request().Context()
- payload := ActivityShortcutCreatePayload{
- Title: shortcut.Title,
- Payload: shortcut.Payload,
- }
- payloadBytes, err := json.Marshal(payload)
- if err != nil {
- return errors.Wrap(err, "failed to marshal activity payload")
- }
- activity, err := s.Store.CreateActivity(ctx, &store.Activity{
- CreatorID: shortcut.CreatorID,
- Type: ActivityShortcutCreate.String(),
- Level: ActivityInfo.String(),
- Payload: string(payloadBytes),
- })
- if err != nil || activity == nil {
- return errors.Wrap(err, "failed to create activity")
- }
- return err
- }
- func convertShortcutFromStore(shortcut *store.Shortcut) *Shortcut {
- return &Shortcut{
- ID: shortcut.ID,
- RowStatus: RowStatus(shortcut.RowStatus),
- CreatorID: shortcut.CreatorID,
- Title: shortcut.Title,
- Payload: shortcut.Payload,
- CreatedTs: shortcut.CreatedTs,
- UpdatedTs: shortcut.UpdatedTs,
- }
- }
|