From 2ca226b0bcf55a7113d9696f0feab48d09241b11 Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 14 Jan 2024 02:28:13 -0800 Subject: [PATCH 1/5] Created endpoint to create .zip of all memos --- api/v1/memo.go | 169 +++++++++++++++++++++++++++++-------------------- 1 file changed, 102 insertions(+), 67 deletions(-) diff --git a/api/v1/memo.go b/api/v1/memo.go index 28ddc9f64cc22..0504ecac661b8 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -1,6 +1,8 @@ package v1 import ( + "archive/zip" + "bytes" "context" "encoding/json" "fmt" @@ -120,6 +122,7 @@ const maxContentLength = 1 << 30 func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { g.GET("/memo", s.GetMemoList) g.POST("/memo", s.CreateMemo) + g.GET("/memo/export", s.ExportMemo) g.GET("/memo/all", s.GetAllMemos) g.GET("/memo/stats", s.GetMemoStats) g.GET("/memo/:memoId", s.GetMemo) @@ -145,84 +148,42 @@ func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { // @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to compose memo response" // @Router /api/v1/memo [GET] func (s *APIV1Service) GetMemoList(c echo.Context) error { - ctx := c.Request().Context() - find := &store.FindMemo{ - OrderByPinned: true, - } - if userID, err := util.ConvertStringToInt32(c.QueryParam("creatorId")); err == nil { - find.CreatorID = &userID - } - - if username := c.QueryParam("creatorUsername"); username != "" { - user, _ := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) - if user != nil { - find.CreatorID = &user.ID - } + list, httpError := s.getMemoList(c) + if httpError != nil { + return httpError } - currentUserID, ok := c.Get(userIDContextKey).(int32) - if !ok { - // Anonymous use should only fetch PUBLIC memos with specified user - if find.CreatorID == nil { - return echo.NewHTTPError(http.StatusBadRequest, "Missing user to find memo") - } - find.VisibilityList = []store.Visibility{store.Public} - } else { - // Authorized user can fetch all PUBLIC/PROTECTED memo - visibilityList := []store.Visibility{store.Public, store.Protected} - - // If Creator is authorized user (as default), PRIVATE memo is OK - if find.CreatorID == nil || *find.CreatorID == currentUserID { - find.CreatorID = ¤tUserID - visibilityList = append(visibilityList, store.Private) + memoResponseList := []*Memo{} + for _, memo := range list { + memoResponse, err := s.convertMemoFromStore(c.Request().Context(), memo) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to compose memo response").SetInternal(err) } - find.VisibilityList = visibilityList - } - - rowStatus := store.RowStatus(c.QueryParam("rowStatus")) - if rowStatus != "" { - find.RowStatus = &rowStatus - } - - contentSearch := []string{} - tag := c.QueryParam("tag") - if tag != "" { - contentSearch = append(contentSearch, "#"+tag) - } - content := c.QueryParam("content") - if content != "" { - contentSearch = append(contentSearch, content) - } - find.ContentSearch = contentSearch - - if limit, err := strconv.Atoi(c.QueryParam("limit")); err == nil { - find.Limit = &limit - } - if offset, err := strconv.Atoi(c.QueryParam("offset")); err == nil { - find.Offset = &offset + memoResponseList = append(memoResponseList, memoResponse) } + return c.JSON(http.StatusOK, memoResponseList) +} - memoDisplayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get memo display with updated ts setting value").SetInternal(err) - } - if memoDisplayWithUpdatedTs { - find.OrderByUpdatedTs = true +func (s *APIV1Service) ExportMemo(c echo.Context) error { + list, httpError := s.getMemoList(c) + if httpError != nil { + return httpError } - list, err := s.Store.ListMemos(ctx, find) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch memo list").SetInternal(err) - } - memoResponseList := []*Memo{} + buf := new(bytes.Buffer) + writer := zip.NewWriter(buf) for _, memo := range list { - memoResponse, err := s.convertMemoFromStore(ctx, memo) + f, err := writer.Create(time.Unix(memo.CreatedTs, 0).Format(time.RFC3339) + ".md") if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to compose memo response").SetInternal(err) + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create memo file").SetInternal(err) } - memoResponseList = append(memoResponseList, memoResponse) + _, err = f.Write([]byte(memo.Content)) } - return c.JSON(http.StatusOK, memoResponseList) + err := writer.Close() + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to close zip file writer").SetInternal(err) + } + return c.Blob(http.StatusOK, "application/zip", buf.Bytes()) } // CreateMemo godoc @@ -948,6 +909,80 @@ func getIDListDiff(oldList, newList []int32) (addedList, removedList []int32) { return addedList, removedList } +func (s *APIV1Service) getMemoList(c echo.Context) ([]*store.Memo, *echo.HTTPError) { + ctx := c.Request().Context() + find := &store.FindMemo{ + OrderByPinned: true, + } + if userID, err := util.ConvertStringToInt32(c.QueryParam("creatorId")); err == nil { + find.CreatorID = &userID + } + + if username := c.QueryParam("creatorUsername"); username != "" { + user, _ := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) + if user != nil { + find.CreatorID = &user.ID + } + } + + currentUserID, ok := c.Get(userIDContextKey).(int32) + if !ok { + // Anonymous use should only fetch PUBLIC memos with specified user + if find.CreatorID == nil { + return nil, echo.NewHTTPError(http.StatusBadRequest, "Missing user to find memo") + } + find.VisibilityList = []store.Visibility{store.Public} + } else { + // Authorized user can fetch all PUBLIC/PROTECTED memo + visibilityList := []store.Visibility{store.Public, store.Protected} + + // If Creator is authorized user (as default), PRIVATE memo is OK + if find.CreatorID == nil || *find.CreatorID == currentUserID { + find.CreatorID = ¤tUserID + visibilityList = append(visibilityList, store.Private) + } + find.VisibilityList = visibilityList + } + + rowStatus := store.RowStatus(c.QueryParam("rowStatus")) + if rowStatus != "" { + find.RowStatus = &rowStatus + } + + contentSearch := []string{} + tag := c.QueryParam("tag") + if tag != "" { + contentSearch = append(contentSearch, "#"+tag) + } + content := c.QueryParam("content") + if content != "" { + contentSearch = append(contentSearch, content) + } + find.ContentSearch = contentSearch + + if limit, err := strconv.Atoi(c.QueryParam("limit")); err == nil { + find.Limit = &limit + } + if offset, err := strconv.Atoi(c.QueryParam("offset")); err == nil { + find.Offset = &offset + } + + memoDisplayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) + if err != nil { + return nil, echo.NewHTTPError(http.StatusInternalServerError, "Failed to get memo display with updated ts setting value").SetInternal(err) + } + if memoDisplayWithUpdatedTs { + find.OrderByUpdatedTs = true + } + + list, err := s.Store.ListMemos(ctx, find) + if err != nil { + return nil, echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch memo list").SetInternal(err) + } + + return list, nil +} + // DispatchMemoCreatedWebhook dispatches webhook when memo is created. func (s *APIV1Service) DispatchMemoCreatedWebhook(ctx context.Context, memo *Memo) error { return s.dispatchMemoRelatedWebhook(ctx, memo, "memos.memo.created") From 7297e624b00bc7b46eb78a1450fc383ff1ff6ffd Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 14 Jan 2024 11:09:25 -0800 Subject: [PATCH 2/5] Add godoc for ExportMemos --- api/v1/memo.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/api/v1/memo.go b/api/v1/memo.go index 0504ecac661b8..56144efdc3a9d 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -122,7 +122,7 @@ const maxContentLength = 1 << 30 func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { g.GET("/memo", s.GetMemoList) g.POST("/memo", s.CreateMemo) - g.GET("/memo/export", s.ExportMemo) + g.GET("/memo/export", s.ExportMemos) g.GET("/memo/all", s.GetAllMemos) g.GET("/memo/stats", s.GetMemoStats) g.GET("/memo/:memoId", s.GetMemo) @@ -164,7 +164,24 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { return c.JSON(http.StatusOK, memoResponseList) } -func (s *APIV1Service) ExportMemo(c echo.Context) error { +// ExportMemos godoc +// +// @Summary Get a zip folder of Markdown files containing memos matching optional filters +// @Tags memo +// @Produce zip folder +// @Param creatorId query int false "Creator ID" +// @Param creatorUsername query string false "Creator username" +// @Param rowStatus query store.RowStatus false "Row status" +// @Param pinned query bool false "Pinned" +// @Param tag query string false "Search for tag. Do not append #" +// @Param content query string false "Search for content" +// @Param limit query int false "Limit" +// @Param offset query int false "Offset" +// @Success 200 {object} []byte "zip folder of Memos Markdown files" +// @Failure 400 {object} nil "Missing user to find memo" +// @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | Failed to close zip file writer" +// @Router /api/v1/memo [GET] +func (s *APIV1Service) ExportMemos(c echo.Context) error { list, httpError := s.getMemoList(c) if httpError != nil { return httpError From 0974f3c677d99a2446627578a9c06788707ee279 Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 14 Jan 2024 13:10:40 -0800 Subject: [PATCH 3/5] Add Export Memos button --- api/v1/docs.go | 44 ++++++++++--------- api/v1/memo.go | 4 +- api/v1/swagger.yaml | 42 ++++++++++-------- .../components/Settings/MyAccountSection.tsx | 13 ++++++ web/src/components/ShareMemoDialog.tsx | 11 ++--- web/src/helpers/api.ts | 4 ++ web/src/helpers/utils.ts | 8 ++++ web/src/locales/en.json | 1 + 8 files changed, 78 insertions(+), 49 deletions(-) diff --git a/api/v1/docs.go b/api/v1/docs.go index b9da4740afec1..4303dc5504b18 100644 --- a/api/v1/docs.go +++ b/api/v1/docs.go @@ -1,5 +1,4 @@ -// Code generated by swaggo/swag. DO NOT EDIT. - +// Package v1 Code generated by swaggo/swag. DO NOT EDIT package v1 import "github.com/swaggo/swag" @@ -42,7 +41,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SignIn" + "$ref": "#/definitions/api_v1.SignIn" } } ], @@ -87,7 +86,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SSOSignIn" + "$ref": "#/definitions/api_v1.SSOSignIn" } } ], @@ -154,7 +153,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SignUp" + "$ref": "#/definitions/api_v1.SignUp" } } ], @@ -380,12 +379,12 @@ const docTemplate = `{ "/api/v1/memo": { "get": { "produces": [ - "application/json" + "application/zip" ], "tags": [ "memo" ], - "summary": "Get a list of memos matching optional filters", + "summary": "Get a zip folder of Markdown files containing memos matching optional filters", "parameters": [ { "type": "integer", @@ -442,11 +441,11 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Memo list", + "description": "zip folder of Memos Markdown files", "schema": { "type": "array", "items": { - "$ref": "#/definitions/store.Memo" + "type": "integer" } } }, @@ -454,7 +453,7 @@ const docTemplate = `{ "description": "Missing user to find memo" }, "500": { - "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to compose memo response" + "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | Failed to close zip file writer" } } }, @@ -477,7 +476,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateMemoRequest" + "$ref": "#/definitions/api_v1.CreateMemoRequest" } } ], @@ -695,7 +694,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.PatchMemoRequest" + "$ref": "#/definitions/api_v1.PatchMemoRequest" } } ], @@ -992,7 +991,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateResourceRequest" + "$ref": "#/definitions/api_v1.CreateResourceRequest" } } ], @@ -1116,7 +1115,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpdateResourceRequest" + "$ref": "#/definitions/api_v1.UpdateResourceRequest" } } ], @@ -1155,7 +1154,7 @@ const docTemplate = `{ "200": { "description": "System GetSystemStatus", "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SystemStatus" + "$ref": "#/definitions/api_v1.SystemStatus" } }, "401": { @@ -1331,7 +1330,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/api_v1.SystemSetting" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.SystemSetting" } } }, @@ -1361,7 +1360,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.UpsertSystemSettingRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertSystemSettingRequest" } } ], @@ -1457,7 +1456,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertTagRequest" + "$ref": "#/definitions/api_v1.UpsertTagRequest" } } ], @@ -1499,7 +1498,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.DeleteTagRequest" + "$ref": "#/definitions/api_v1.DeleteTagRequest" } } ], @@ -1592,7 +1591,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateUserRequest" + "$ref": "#/definitions/api_v1.CreateUserRequest" } } ], @@ -1773,7 +1772,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpdateUserRequest" + "$ref": "#/definitions/api_v1.UpdateUserRequest" } } ], @@ -3253,6 +3252,9 @@ const docTemplate = `{ "id": { "type": "integer" }, + "parentID": { + "type": "integer" + }, "pinned": { "description": "Composed fields", "type": "boolean" diff --git a/api/v1/memo.go b/api/v1/memo.go index 56144efdc3a9d..1da4186963b87 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -168,7 +168,7 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { // // @Summary Get a zip folder of Markdown files containing memos matching optional filters // @Tags memo -// @Produce zip folder +// @Produce application/zip // @Param creatorId query int false "Creator ID" // @Param creatorUsername query string false "Creator username" // @Param rowStatus query store.RowStatus false "Row status" @@ -177,7 +177,7 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { // @Param content query string false "Search for content" // @Param limit query int false "Limit" // @Param offset query int false "Offset" -// @Success 200 {object} []byte "zip folder of Memos Markdown files" +// @Success 200 {object} []byte "zip folder of Memos Markdown files" // @Failure 400 {object} nil "Missing user to find memo" // @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | Failed to close zip file writer" // @Router /api/v1/memo [GET] diff --git a/api/v1/swagger.yaml b/api/v1/swagger.yaml index e3576e23cd54a..0c97c1dd97feb 100644 --- a/api/v1/swagger.yaml +++ b/api/v1/swagger.yaml @@ -932,6 +932,8 @@ definitions: type: integer id: type: integer + parentID: + type: integer pinned: description: Composed fields type: boolean @@ -1088,7 +1090,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SignIn' + $ref: '#/definitions/api_v1.SignIn' produces: - application/json responses: @@ -1120,7 +1122,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SSOSignIn' + $ref: '#/definitions/api_v1.SSOSignIn' produces: - application/json responses: @@ -1168,7 +1170,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SignUp' + $ref: '#/definitions/api_v1.SignUp' produces: - application/json responses: @@ -1359,20 +1361,22 @@ paths: name: offset type: integer produces: - - application/json + - application/zip responses: "200": - description: Memo list + description: zip folder of Memos Markdown files schema: items: - $ref: '#/definitions/store.Memo' + type: integer type: array "400": description: Missing user to find memo "500": description: Failed to get memo display with updated ts setting value | - Failed to fetch memo list | Failed to compose memo response - summary: Get a list of memos matching optional filters + Failed to fetch memo list | Failed to create memo file | Failed to close + zip file writer + summary: Get a zip folder of Markdown files containing memos matching optional + filters tags: - memo post: @@ -1387,7 +1391,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateMemoRequest' + $ref: '#/definitions/api_v1.CreateMemoRequest' produces: - application/json responses: @@ -1484,7 +1488,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.PatchMemoRequest' + $ref: '#/definitions/api_v1.PatchMemoRequest' produces: - application/json responses: @@ -1743,7 +1747,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateResourceRequest' + $ref: '#/definitions/api_v1.CreateResourceRequest' produces: - application/json responses: @@ -1801,7 +1805,7 @@ paths: name: patch required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpdateResourceRequest' + $ref: '#/definitions/api_v1.UpdateResourceRequest' produces: - application/json responses: @@ -1856,7 +1860,7 @@ paths: "200": description: System GetSystemStatus schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SystemStatus' + $ref: '#/definitions/api_v1.SystemStatus' "401": description: Missing user in session | Unauthorized "500": @@ -1975,7 +1979,7 @@ paths: description: System setting list schema: items: - $ref: '#/definitions/api_v1.SystemSetting' + $ref: '#/definitions/github_com_usememos_memos_api_v1.SystemSetting' type: array "401": description: Missing user in session | Unauthorized @@ -1993,7 +1997,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.UpsertSystemSettingRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertSystemSettingRequest' produces: - application/json responses: @@ -2055,7 +2059,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertTagRequest' + $ref: '#/definitions/api_v1.UpsertTagRequest' produces: - application/json responses: @@ -2082,7 +2086,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.DeleteTagRequest' + $ref: '#/definitions/api_v1.DeleteTagRequest' produces: - application/json responses: @@ -2142,7 +2146,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateUserRequest' + $ref: '#/definitions/api_v1.CreateUserRequest' produces: - application/json responses: @@ -2224,7 +2228,7 @@ paths: name: patch required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpdateUserRequest' + $ref: '#/definitions/api_v1.UpdateUserRequest' produces: - application/json responses: diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index 53c4ae34e94e2..b57c1089c0ca6 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -5,6 +5,8 @@ import showChangePasswordDialog from "../ChangePasswordDialog"; import showUpdateAccountDialog from "../UpdateAccountDialog"; import UserAvatar from "../UserAvatar"; import AccessTokenSection from "./AccessTokenSection"; +import * as api from "@/helpers/api"; +import { downloadFileFromUrl } from "@/helpers/utils"; const MyAccountSection = () => { const t = useTranslate(); @@ -27,6 +29,9 @@ const MyAccountSection = () => { <Button variant="outlined" onClick={showChangePasswordDialog}> {t("setting.account-section.change-password")} </Button> + <Button variant="outlined" onClick={downloadExportedMemos}> + {t("setting.account-section.export-memos")} + </Button> </div> <AccessTokenSection /> @@ -34,4 +39,12 @@ const MyAccountSection = () => { ); }; +const downloadExportedMemos = () => { + api.exportMemos().then(response => { + const url = window.URL.createObjectURL(new Blob([response.data])); + downloadFileFromUrl(url, "memos-export.zip"); + URL.revokeObjectURL(url); + }) +} + export default MyAccountSection; diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index 305a02691017d..ff82828091e18 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -14,6 +14,7 @@ import MemoContent from "./MemoContent"; import MemoResourceListView from "./MemoResourceListView"; import UserAvatar from "./UserAvatar"; import "@/less/share-memo-dialog.less"; +import { downloadFileFromUrl } from "@/helpers/utils"; interface Props extends DialogProps { memo: Memo; @@ -51,6 +52,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => { .then((url) => { downloadFileFromUrl(url, `memos-${getDateTimeString(Date.now())}.png`); downloadingImageState.setFinish(); + URL.revokeObjectURL(url); }) .catch((err) => { console.error(err); @@ -59,14 +61,9 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => { const handleDownloadTextFileBtnClick = () => { const blob = new Blob([memo.content], { type: "text/plain;charset=utf-8" }); + const url = URL.createObjectURL(blob); downloadFileFromUrl(URL.createObjectURL(blob), `memos-${getDateTimeString(Date.now())}.md`); - }; - - const downloadFileFromUrl = (url: string, filename: string) => { - const a = document.createElement("a"); - a.href = url; - a.download = filename; - a.click(); + URL.revokeObjectURL(url); }; const handleCopyLinkBtnClick = () => { diff --git a/web/src/helpers/api.ts b/web/src/helpers/api.ts index 18384b819648f..a24dc03537f59 100644 --- a/web/src/helpers/api.ts +++ b/web/src/helpers/api.ts @@ -13,6 +13,10 @@ export function upsertSystemSetting(systemSetting: SystemSetting) { return axios.post<SystemSetting>("/api/v1/system/setting", systemSetting); } +export function exportMemos() { + return axios.get("/api/v1/memo/export", {responseType: 'blob'}); +} + export function vacuumDatabase() { return axios.post("/api/v1/system/vacuum"); } diff --git a/web/src/helpers/utils.ts b/web/src/helpers/utils.ts index b03b20c83d2da..7613dc64fb07e 100644 --- a/web/src/helpers/utils.ts +++ b/web/src/helpers/utils.ts @@ -83,3 +83,11 @@ export const formatBytes = (bytes: number) => { i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; }; + +export const downloadFileFromUrl = (url: string, filename: string) => { + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + a.remove(); +}; diff --git a/web/src/locales/en.json b/web/src/locales/en.json index f9577ef59511a..8ffa80b1fdf58 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -176,6 +176,7 @@ "email-note": "Optional", "update-information": "Update Information", "change-password": "Change password", + "export-memos": "Export Memos", "reset-api": "Reset API", "openapi-title": "OpenAPI", "openapi-reset": "Reset OpenAPI Key", From 3cdc73d767c139fec106066a12eca6e61193745c Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 14 Jan 2024 13:42:35 -0800 Subject: [PATCH 4/5] Resolve style issues --- api/v1/docs.go | 24 ++++++++--------- api/v1/memo.go | 11 +++++--- api/v1/swagger.yaml | 26 +++++++++---------- .../components/Settings/MyAccountSection.tsx | 16 ++++++------ web/src/components/ShareMemoDialog.tsx | 2 +- web/src/helpers/api.ts | 2 +- 6 files changed, 42 insertions(+), 39 deletions(-) diff --git a/api/v1/docs.go b/api/v1/docs.go index 4303dc5504b18..523a621ecb918 100644 --- a/api/v1/docs.go +++ b/api/v1/docs.go @@ -41,7 +41,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.SignIn" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.SignIn" } } ], @@ -86,7 +86,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.SSOSignIn" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.SSOSignIn" } } ], @@ -153,7 +153,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.SignUp" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.SignUp" } } ], @@ -453,7 +453,7 @@ const docTemplate = `{ "description": "Missing user to find memo" }, "500": { - "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | Failed to close zip file writer" + "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | \"Failed to write to memo file | Failed to close zip file writer" } } }, @@ -476,7 +476,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.CreateMemoRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateMemoRequest" } } ], @@ -694,7 +694,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.PatchMemoRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.PatchMemoRequest" } } ], @@ -746,7 +746,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertMemoOrganizerRequest" + "$ref": "#/definitions/api_v1.UpsertMemoOrganizerRequest" } } ], @@ -837,7 +837,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.UpsertMemoRelationRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertMemoRelationRequest" } } ], @@ -991,7 +991,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.CreateResourceRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateResourceRequest" } } ], @@ -1115,7 +1115,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.UpdateResourceRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpdateResourceRequest" } } ], @@ -1591,7 +1591,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.CreateUserRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateUserRequest" } } ], @@ -1772,7 +1772,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.UpdateUserRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpdateUserRequest" } } ], diff --git a/api/v1/memo.go b/api/v1/memo.go index 1da4186963b87..c7ca7193948f7 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -148,7 +148,7 @@ func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { // @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to compose memo response" // @Router /api/v1/memo [GET] func (s *APIV1Service) GetMemoList(c echo.Context) error { - list, httpError := s.getMemoList(c) + list, httpError := s.getMemosAsList(c) if httpError != nil { return httpError } @@ -179,10 +179,10 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { // @Param offset query int false "Offset" // @Success 200 {object} []byte "zip folder of Memos Markdown files" // @Failure 400 {object} nil "Missing user to find memo" -// @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | Failed to close zip file writer" +// @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | "Failed to write to memo file | Failed to close zip file writer" // @Router /api/v1/memo [GET] func (s *APIV1Service) ExportMemos(c echo.Context) error { - list, httpError := s.getMemoList(c) + list, httpError := s.getMemosAsList(c) if httpError != nil { return httpError } @@ -195,6 +195,9 @@ func (s *APIV1Service) ExportMemos(c echo.Context) error { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create memo file").SetInternal(err) } _, err = f.Write([]byte(memo.Content)) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to write to memo file").SetInternal(err) + } } err := writer.Close() if err != nil { @@ -926,7 +929,7 @@ func getIDListDiff(oldList, newList []int32) (addedList, removedList []int32) { return addedList, removedList } -func (s *APIV1Service) getMemoList(c echo.Context) ([]*store.Memo, *echo.HTTPError) { +func (s *APIV1Service) getMemosAsList(c echo.Context) ([]*store.Memo, *echo.HTTPError) { ctx := c.Request().Context() find := &store.FindMemo{ OrderByPinned: true, diff --git a/api/v1/swagger.yaml b/api/v1/swagger.yaml index 0c97c1dd97feb..5feea7a152684 100644 --- a/api/v1/swagger.yaml +++ b/api/v1/swagger.yaml @@ -1090,7 +1090,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.SignIn' + $ref: '#/definitions/github_com_usememos_memos_api_v1.SignIn' produces: - application/json responses: @@ -1122,7 +1122,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.SSOSignIn' + $ref: '#/definitions/github_com_usememos_memos_api_v1.SSOSignIn' produces: - application/json responses: @@ -1170,7 +1170,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.SignUp' + $ref: '#/definitions/github_com_usememos_memos_api_v1.SignUp' produces: - application/json responses: @@ -1373,8 +1373,8 @@ paths: description: Missing user to find memo "500": description: Failed to get memo display with updated ts setting value | - Failed to fetch memo list | Failed to create memo file | Failed to close - zip file writer + Failed to fetch memo list | Failed to create memo file | "Failed to write + to memo file | Failed to close zip file writer summary: Get a zip folder of Markdown files containing memos matching optional filters tags: @@ -1391,7 +1391,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.CreateMemoRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateMemoRequest' produces: - application/json responses: @@ -1488,7 +1488,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.PatchMemoRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.PatchMemoRequest' produces: - application/json responses: @@ -1525,7 +1525,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertMemoOrganizerRequest' + $ref: '#/definitions/api_v1.UpsertMemoOrganizerRequest' produces: - application/json responses: @@ -1587,7 +1587,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.UpsertMemoRelationRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertMemoRelationRequest' produces: - application/json responses: @@ -1747,7 +1747,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.CreateResourceRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateResourceRequest' produces: - application/json responses: @@ -1805,7 +1805,7 @@ paths: name: patch required: true schema: - $ref: '#/definitions/api_v1.UpdateResourceRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.UpdateResourceRequest' produces: - application/json responses: @@ -2146,7 +2146,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.CreateUserRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateUserRequest' produces: - application/json responses: @@ -2228,7 +2228,7 @@ paths: name: patch required: true schema: - $ref: '#/definitions/api_v1.UpdateUserRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.UpdateUserRequest' produces: - application/json responses: diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index b57c1089c0ca6..bd8a95da8199b 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -1,12 +1,12 @@ import { Button } from "@mui/joy"; +import * as api from "@/helpers/api"; +import { downloadFileFromUrl } from "@/helpers/utils"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useTranslate } from "@/utils/i18n"; import showChangePasswordDialog from "../ChangePasswordDialog"; import showUpdateAccountDialog from "../UpdateAccountDialog"; import UserAvatar from "../UserAvatar"; import AccessTokenSection from "./AccessTokenSection"; -import * as api from "@/helpers/api"; -import { downloadFileFromUrl } from "@/helpers/utils"; const MyAccountSection = () => { const t = useTranslate(); @@ -40,11 +40,11 @@ const MyAccountSection = () => { }; const downloadExportedMemos = () => { - api.exportMemos().then(response => { - const url = window.URL.createObjectURL(new Blob([response.data])); - downloadFileFromUrl(url, "memos-export.zip"); - URL.revokeObjectURL(url); - }) -} + api.exportMemos().then((response) => { + const url = window.URL.createObjectURL(new Blob([response.data])); + downloadFileFromUrl(url, "memos-export.zip"); + URL.revokeObjectURL(url); + }); +}; export default MyAccountSection; diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index ff82828091e18..1d6528f1f741c 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -3,6 +3,7 @@ import copy from "copy-to-clipboard"; import React, { useEffect, useRef } from "react"; import { toast } from "react-hot-toast"; import { getDateTimeString } from "@/helpers/datetime"; +import { downloadFileFromUrl } from "@/helpers/utils"; import useLoading from "@/hooks/useLoading"; import toImage from "@/labs/html2image"; import { useUserStore, extractUsernameFromName } from "@/store/v1"; @@ -14,7 +15,6 @@ import MemoContent from "./MemoContent"; import MemoResourceListView from "./MemoResourceListView"; import UserAvatar from "./UserAvatar"; import "@/less/share-memo-dialog.less"; -import { downloadFileFromUrl } from "@/helpers/utils"; interface Props extends DialogProps { memo: Memo; diff --git a/web/src/helpers/api.ts b/web/src/helpers/api.ts index a24dc03537f59..b721f2880231b 100644 --- a/web/src/helpers/api.ts +++ b/web/src/helpers/api.ts @@ -14,7 +14,7 @@ export function upsertSystemSetting(systemSetting: SystemSetting) { } export function exportMemos() { - return axios.get("/api/v1/memo/export", {responseType: 'blob'}); + return axios.get("/api/v1/memo/export", { responseType: "blob" }); } export function vacuumDatabase() { From 5a44e38a6cec5d6380cc082ecf6569cb651605d8 Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 14 Jan 2024 13:44:03 -0800 Subject: [PATCH 5/5] Fix incorrect docs --- api/v1/docs.go | 118 +++++++++++++++++++++++++++++++++++++------- api/v1/memo.go | 2 +- api/v1/swagger.yaml | 95 +++++++++++++++++++++++++++-------- 3 files changed, 176 insertions(+), 39 deletions(-) diff --git a/api/v1/docs.go b/api/v1/docs.go index 523a621ecb918..ec497356943f6 100644 --- a/api/v1/docs.go +++ b/api/v1/docs.go @@ -41,7 +41,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SignIn" + "$ref": "#/definitions/api_v1.SignIn" } } ], @@ -86,7 +86,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SSOSignIn" + "$ref": "#/definitions/api_v1.SSOSignIn" } } ], @@ -153,7 +153,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SignUp" + "$ref": "#/definitions/api_v1.SignUp" } } ], @@ -198,7 +198,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.IdentityProvider" + "$ref": "#/definitions/api_v1.IdentityProvider" } } }, @@ -225,7 +225,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateIdentityProviderRequest" + "$ref": "#/definitions/api_v1.CreateIdentityProviderRequest" } } ], @@ -353,7 +353,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpdateIdentityProviderRequest" + "$ref": "#/definitions/api_v1.UpdateIdentityProviderRequest" } } ], @@ -379,12 +379,12 @@ const docTemplate = `{ "/api/v1/memo": { "get": { "produces": [ - "application/zip" + "application/json" ], "tags": [ "memo" ], - "summary": "Get a zip folder of Markdown files containing memos matching optional filters", + "summary": "Get a list of memos matching optional filters", "parameters": [ { "type": "integer", @@ -441,11 +441,11 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "zip folder of Memos Markdown files", + "description": "Memo list", "schema": { "type": "array", "items": { - "type": "integer" + "$ref": "#/definitions/store.Memo" } } }, @@ -453,7 +453,7 @@ const docTemplate = `{ "description": "Missing user to find memo" }, "500": { - "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | \"Failed to write to memo file | Failed to close zip file writer" + "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to compose memo response" } } }, @@ -542,6 +542,88 @@ const docTemplate = `{ } } }, + "/api/v1/memo/export": { + "get": { + "produces": [ + "application/zip" + ], + "tags": [ + "memo" + ], + "summary": "Get a zip folder of Markdown files containing memos matching optional filters", + "parameters": [ + { + "type": "integer", + "description": "Creator ID", + "name": "creatorId", + "in": "query" + }, + { + "type": "string", + "description": "Creator username", + "name": "creatorUsername", + "in": "query" + }, + { + "enum": [ + "NORMAL", + "ARCHIVED" + ], + "type": "string", + "description": "Row status", + "name": "rowStatus", + "in": "query" + }, + { + "type": "boolean", + "description": "Pinned", + "name": "pinned", + "in": "query" + }, + { + "type": "string", + "description": "Search for tag. Do not append #", + "name": "tag", + "in": "query" + }, + { + "type": "string", + "description": "Search for content", + "name": "content", + "in": "query" + }, + { + "type": "integer", + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "zip folder of Memos Markdown files", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "400": { + "description": "Missing user to find memo" + }, + "500": { + "description": "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | \"Failed to write to memo file | Failed to close zip file writer" + } + } + } + }, "/api/v1/memo/stats": { "get": { "description": "Used to generate the heatmap", @@ -746,7 +828,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api_v1.UpsertMemoOrganizerRequest" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertMemoOrganizerRequest" } } ], @@ -837,7 +919,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertMemoRelationRequest" + "$ref": "#/definitions/api_v1.UpsertMemoRelationRequest" } } ], @@ -991,7 +1073,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.CreateResourceRequest" + "$ref": "#/definitions/api_v1.CreateResourceRequest" } } ], @@ -1115,7 +1197,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpdateResourceRequest" + "$ref": "#/definitions/api_v1.UpdateResourceRequest" } } ], @@ -1154,7 +1236,7 @@ const docTemplate = `{ "200": { "description": "System GetSystemStatus", "schema": { - "$ref": "#/definitions/api_v1.SystemStatus" + "$ref": "#/definitions/github_com_usememos_memos_api_v1.SystemStatus" } }, "401": { @@ -1330,7 +1412,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.SystemSetting" + "$ref": "#/definitions/api_v1.SystemSetting" } } }, @@ -1360,7 +1442,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_usememos_memos_api_v1.UpsertSystemSettingRequest" + "$ref": "#/definitions/api_v1.UpsertSystemSettingRequest" } } ], diff --git a/api/v1/memo.go b/api/v1/memo.go index c7ca7193948f7..01b6702d6220b 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -180,7 +180,7 @@ func (s *APIV1Service) GetMemoList(c echo.Context) error { // @Success 200 {object} []byte "zip folder of Memos Markdown files" // @Failure 400 {object} nil "Missing user to find memo" // @Failure 500 {object} nil "Failed to get memo display with updated ts setting value | Failed to fetch memo list | Failed to create memo file | "Failed to write to memo file | Failed to close zip file writer" -// @Router /api/v1/memo [GET] +// @Router /api/v1/memo/export [GET] func (s *APIV1Service) ExportMemos(c echo.Context) error { list, httpError := s.getMemosAsList(c) if httpError != nil { diff --git a/api/v1/swagger.yaml b/api/v1/swagger.yaml index 5feea7a152684..cd2e04606305d 100644 --- a/api/v1/swagger.yaml +++ b/api/v1/swagger.yaml @@ -1090,7 +1090,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SignIn' + $ref: '#/definitions/api_v1.SignIn' produces: - application/json responses: @@ -1122,7 +1122,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SSOSignIn' + $ref: '#/definitions/api_v1.SSOSignIn' produces: - application/json responses: @@ -1170,7 +1170,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SignUp' + $ref: '#/definitions/api_v1.SignUp' produces: - application/json responses: @@ -1203,7 +1203,7 @@ paths: description: List of available identity providers schema: items: - $ref: '#/definitions/github_com_usememos_memos_api_v1.IdentityProvider' + $ref: '#/definitions/api_v1.IdentityProvider' type: array "500": description: Failed to find identity provider list | Failed to find user @@ -1219,7 +1219,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateIdentityProviderRequest' + $ref: '#/definitions/api_v1.CreateIdentityProviderRequest' produces: - application/json responses: @@ -1304,7 +1304,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpdateIdentityProviderRequest' + $ref: '#/definitions/api_v1.UpdateIdentityProviderRequest' produces: - application/json responses: @@ -1361,22 +1361,20 @@ paths: name: offset type: integer produces: - - application/zip + - application/json responses: "200": - description: zip folder of Memos Markdown files + description: Memo list schema: items: - type: integer + $ref: '#/definitions/store.Memo' type: array "400": description: Missing user to find memo "500": description: Failed to get memo display with updated ts setting value | - Failed to fetch memo list | Failed to create memo file | "Failed to write - to memo file | Failed to close zip file writer - summary: Get a zip folder of Markdown files containing memos matching optional - filters + Failed to fetch memo list | Failed to compose memo response + summary: Get a list of memos matching optional filters tags: - memo post: @@ -1525,7 +1523,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/api_v1.UpsertMemoOrganizerRequest' + $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertMemoOrganizerRequest' produces: - application/json responses: @@ -1587,7 +1585,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertMemoRelationRequest' + $ref: '#/definitions/api_v1.UpsertMemoRelationRequest' produces: - application/json responses: @@ -1670,6 +1668,63 @@ paths: summary: Get a list of public memos matching optional filters tags: - memo + /api/v1/memo/export: + get: + parameters: + - description: Creator ID + in: query + name: creatorId + type: integer + - description: Creator username + in: query + name: creatorUsername + type: string + - description: Row status + enum: + - NORMAL + - ARCHIVED + in: query + name: rowStatus + type: string + - description: Pinned + in: query + name: pinned + type: boolean + - description: 'Search for tag. Do not append #' + in: query + name: tag + type: string + - description: Search for content + in: query + name: content + type: string + - description: Limit + in: query + name: limit + type: integer + - description: Offset + in: query + name: offset + type: integer + produces: + - application/zip + responses: + "200": + description: zip folder of Memos Markdown files + schema: + items: + type: integer + type: array + "400": + description: Missing user to find memo + "500": + description: Failed to get memo display with updated ts setting value | + Failed to fetch memo list | Failed to create memo file | "Failed to write + to memo file | Failed to close zip file writer + summary: Get a zip folder of Markdown files containing memos matching optional + filters + tags: + - memo /api/v1/memo/stats: get: description: Used to generate the heatmap @@ -1747,7 +1802,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.CreateResourceRequest' + $ref: '#/definitions/api_v1.CreateResourceRequest' produces: - application/json responses: @@ -1805,7 +1860,7 @@ paths: name: patch required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpdateResourceRequest' + $ref: '#/definitions/api_v1.UpdateResourceRequest' produces: - application/json responses: @@ -1860,7 +1915,7 @@ paths: "200": description: System GetSystemStatus schema: - $ref: '#/definitions/api_v1.SystemStatus' + $ref: '#/definitions/github_com_usememos_memos_api_v1.SystemStatus' "401": description: Missing user in session | Unauthorized "500": @@ -1979,7 +2034,7 @@ paths: description: System setting list schema: items: - $ref: '#/definitions/github_com_usememos_memos_api_v1.SystemSetting' + $ref: '#/definitions/api_v1.SystemSetting' type: array "401": description: Missing user in session | Unauthorized @@ -1997,7 +2052,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/github_com_usememos_memos_api_v1.UpsertSystemSettingRequest' + $ref: '#/definitions/api_v1.UpsertSystemSettingRequest' produces: - application/json responses: