shortcut.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. package store
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "strings"
  7. "github.com/usememos/memos/api"
  8. "github.com/usememos/memos/common"
  9. )
  10. // shortcutRaw is the store model for an Shortcut.
  11. // Fields have exactly the same meanings as Shortcut.
  12. type shortcutRaw struct {
  13. ID int
  14. // Standard fields
  15. RowStatus api.RowStatus
  16. CreatorID int
  17. CreatedTs int64
  18. UpdatedTs int64
  19. // Domain specific fields
  20. Title string
  21. Payload string
  22. }
  23. func (raw *shortcutRaw) toShortcut() *api.Shortcut {
  24. return &api.Shortcut{
  25. ID: raw.ID,
  26. RowStatus: raw.RowStatus,
  27. CreatorID: raw.CreatorID,
  28. CreatedTs: raw.CreatedTs,
  29. UpdatedTs: raw.UpdatedTs,
  30. Title: raw.Title,
  31. Payload: raw.Payload,
  32. }
  33. }
  34. func (s *Store) CreateShortcut(ctx context.Context, create *api.ShortcutCreate) (*api.Shortcut, error) {
  35. tx, err := s.db.BeginTx(ctx, nil)
  36. if err != nil {
  37. return nil, FormatError(err)
  38. }
  39. defer tx.Rollback()
  40. shortcutRaw, err := createShortcut(ctx, tx, create)
  41. if err != nil {
  42. return nil, err
  43. }
  44. if err := tx.Commit(); err != nil {
  45. return nil, FormatError(err)
  46. }
  47. if err := s.cache.UpsertCache(api.ShortcutCache, shortcutRaw.ID, shortcutRaw); err != nil {
  48. return nil, err
  49. }
  50. shortcut := shortcutRaw.toShortcut()
  51. return shortcut, nil
  52. }
  53. func (s *Store) PatchShortcut(ctx context.Context, patch *api.ShortcutPatch) (*api.Shortcut, error) {
  54. tx, err := s.db.BeginTx(ctx, nil)
  55. if err != nil {
  56. return nil, FormatError(err)
  57. }
  58. defer tx.Rollback()
  59. shortcutRaw, err := patchShortcut(ctx, tx, patch)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if err := tx.Commit(); err != nil {
  64. return nil, FormatError(err)
  65. }
  66. if err := s.cache.UpsertCache(api.ShortcutCache, shortcutRaw.ID, shortcutRaw); err != nil {
  67. return nil, err
  68. }
  69. shortcut := shortcutRaw.toShortcut()
  70. return shortcut, nil
  71. }
  72. func (s *Store) FindShortcutList(ctx context.Context, find *api.ShortcutFind) ([]*api.Shortcut, error) {
  73. tx, err := s.db.BeginTx(ctx, nil)
  74. if err != nil {
  75. return nil, FormatError(err)
  76. }
  77. defer tx.Rollback()
  78. shortcutRawList, err := findShortcutList(ctx, tx, find)
  79. if err != nil {
  80. return nil, err
  81. }
  82. list := []*api.Shortcut{}
  83. for _, raw := range shortcutRawList {
  84. list = append(list, raw.toShortcut())
  85. }
  86. return list, nil
  87. }
  88. func (s *Store) FindShortcut(ctx context.Context, find *api.ShortcutFind) (*api.Shortcut, error) {
  89. if find.ID != nil {
  90. shortcutRaw := &shortcutRaw{}
  91. has, err := s.cache.FindCache(api.ShortcutCache, *find.ID, shortcutRaw)
  92. if err != nil {
  93. return nil, err
  94. }
  95. if has {
  96. return shortcutRaw.toShortcut(), nil
  97. }
  98. }
  99. tx, err := s.db.BeginTx(ctx, nil)
  100. if err != nil {
  101. return nil, FormatError(err)
  102. }
  103. defer tx.Rollback()
  104. list, err := findShortcutList(ctx, tx, find)
  105. if err != nil {
  106. return nil, err
  107. }
  108. if len(list) == 0 {
  109. return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
  110. }
  111. shortcutRaw := list[0]
  112. if err := s.cache.UpsertCache(api.ShortcutCache, shortcutRaw.ID, shortcutRaw); err != nil {
  113. return nil, err
  114. }
  115. shortcut := shortcutRaw.toShortcut()
  116. return shortcut, nil
  117. }
  118. func (s *Store) DeleteShortcut(ctx context.Context, delete *api.ShortcutDelete) error {
  119. tx, err := s.db.BeginTx(ctx, nil)
  120. if err != nil {
  121. return FormatError(err)
  122. }
  123. defer tx.Rollback()
  124. err = deleteShortcut(ctx, tx, delete)
  125. if err != nil {
  126. return FormatError(err)
  127. }
  128. if err := tx.Commit(); err != nil {
  129. return FormatError(err)
  130. }
  131. s.cache.DeleteCache(api.ShortcutCache, *delete.ID)
  132. return nil
  133. }
  134. func createShortcut(ctx context.Context, tx *sql.Tx, create *api.ShortcutCreate) (*shortcutRaw, error) {
  135. query := `
  136. INSERT INTO shortcut (
  137. title,
  138. payload,
  139. creator_id
  140. )
  141. VALUES (?, ?, ?)
  142. RETURNING id, title, payload, creator_id, created_ts, updated_ts, row_status
  143. `
  144. var shortcutRaw shortcutRaw
  145. if err := tx.QueryRowContext(ctx, query, create.Title, create.Payload, create.CreatorID).Scan(
  146. &shortcutRaw.ID,
  147. &shortcutRaw.Title,
  148. &shortcutRaw.Payload,
  149. &shortcutRaw.CreatorID,
  150. &shortcutRaw.CreatedTs,
  151. &shortcutRaw.UpdatedTs,
  152. &shortcutRaw.RowStatus,
  153. ); err != nil {
  154. return nil, FormatError(err)
  155. }
  156. return &shortcutRaw, nil
  157. }
  158. func patchShortcut(ctx context.Context, tx *sql.Tx, patch *api.ShortcutPatch) (*shortcutRaw, error) {
  159. set, args := []string{}, []interface{}{}
  160. if v := patch.UpdatedTs; v != nil {
  161. set, args = append(set, "updated_ts = ?"), append(args, *v)
  162. }
  163. if v := patch.Title; v != nil {
  164. set, args = append(set, "title = ?"), append(args, *v)
  165. }
  166. if v := patch.Payload; v != nil {
  167. set, args = append(set, "payload = ?"), append(args, *v)
  168. }
  169. if v := patch.RowStatus; v != nil {
  170. set, args = append(set, "row_status = ?"), append(args, *v)
  171. }
  172. args = append(args, patch.ID)
  173. query := `
  174. UPDATE shortcut
  175. SET ` + strings.Join(set, ", ") + `
  176. WHERE id = ?
  177. RETURNING id, title, payload, created_ts, updated_ts, row_status
  178. `
  179. var shortcutRaw shortcutRaw
  180. if err := tx.QueryRowContext(ctx, query, args...).Scan(
  181. &shortcutRaw.ID,
  182. &shortcutRaw.Title,
  183. &shortcutRaw.Payload,
  184. &shortcutRaw.CreatedTs,
  185. &shortcutRaw.UpdatedTs,
  186. &shortcutRaw.RowStatus,
  187. ); err != nil {
  188. return nil, FormatError(err)
  189. }
  190. return &shortcutRaw, nil
  191. }
  192. func findShortcutList(ctx context.Context, tx *sql.Tx, find *api.ShortcutFind) ([]*shortcutRaw, error) {
  193. where, args := []string{"1 = 1"}, []interface{}{}
  194. if v := find.ID; v != nil {
  195. where, args = append(where, "id = ?"), append(args, *v)
  196. }
  197. if v := find.CreatorID; v != nil {
  198. where, args = append(where, "creator_id = ?"), append(args, *v)
  199. }
  200. if v := find.Title; v != nil {
  201. where, args = append(where, "title = ?"), append(args, *v)
  202. }
  203. rows, err := tx.QueryContext(ctx, `
  204. SELECT
  205. id,
  206. title,
  207. payload,
  208. creator_id,
  209. created_ts,
  210. updated_ts,
  211. row_status
  212. FROM shortcut
  213. WHERE `+strings.Join(where, " AND ")+`
  214. ORDER BY created_ts DESC`,
  215. args...,
  216. )
  217. if err != nil {
  218. return nil, FormatError(err)
  219. }
  220. defer rows.Close()
  221. shortcutRawList := make([]*shortcutRaw, 0)
  222. for rows.Next() {
  223. var shortcutRaw shortcutRaw
  224. if err := rows.Scan(
  225. &shortcutRaw.ID,
  226. &shortcutRaw.Title,
  227. &shortcutRaw.Payload,
  228. &shortcutRaw.CreatorID,
  229. &shortcutRaw.CreatedTs,
  230. &shortcutRaw.UpdatedTs,
  231. &shortcutRaw.RowStatus,
  232. ); err != nil {
  233. return nil, FormatError(err)
  234. }
  235. shortcutRawList = append(shortcutRawList, &shortcutRaw)
  236. }
  237. if err := rows.Err(); err != nil {
  238. return nil, FormatError(err)
  239. }
  240. return shortcutRawList, nil
  241. }
  242. func deleteShortcut(ctx context.Context, tx *sql.Tx, delete *api.ShortcutDelete) error {
  243. where, args := []string{}, []interface{}{}
  244. if v := delete.ID; v != nil {
  245. where, args = append(where, "id = ?"), append(args, *v)
  246. }
  247. if v := delete.CreatorID; v != nil {
  248. where, args = append(where, "creator_id = ?"), append(args, *v)
  249. }
  250. stmt := `DELETE FROM shortcut WHERE ` + strings.Join(where, " AND ")
  251. result, err := tx.ExecContext(ctx, stmt, args...)
  252. if err != nil {
  253. return FormatError(err)
  254. }
  255. rows, _ := result.RowsAffected()
  256. if rows == 0 {
  257. return &common.Error{Code: common.NotFound, Err: fmt.Errorf("shortcut not found")}
  258. }
  259. return nil
  260. }
  261. func vacuumShortcut(ctx context.Context, tx *sql.Tx) error {
  262. stmt := `
  263. DELETE FROM
  264. shortcut
  265. WHERE
  266. creator_id NOT IN (
  267. SELECT
  268. id
  269. FROM
  270. user
  271. )`
  272. _, err := tx.ExecContext(ctx, stmt)
  273. if err != nil {
  274. return FormatError(err)
  275. }
  276. return nil
  277. }