Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
168c4f69
Unverified
Commit
168c4f69
authored
Feb 09, 2023
by
Stephen Zhou
Committed by
GitHub
Feb 09, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: more rss info (#1052)
* feat: more rss info * fix: ci
parent
3e40b9df
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
155 additions
and
28 deletions
+155
-28
rss.go
server/rss.go
+139
-27
MemosHeader.tsx
web/src/components/MemosHeader.tsx
+8
-1
Explore.tsx
web/src/pages/Explore.tsx
+4
-0
vite.config.ts
web/vite.config.ts
+4
-0
No files found.
server/rss.go
View file @
168c4f69
package
server
package
server
import
(
import
(
"context"
"encoding/json"
"net/http"
"net/http"
"strconv"
"strconv"
"strings"
"time"
"time"
"github.com/gorilla/feeds"
"github.com/gorilla/feeds"
...
@@ -10,10 +13,94 @@ import (
...
@@ -10,10 +13,94 @@ import (
"github.com/usememos/memos/api"
"github.com/usememos/memos/api"
)
)
func
generateRSSFromMemoList
(
memoList
[]
*
api
.
Memo
,
baseURL
string
,
profile
*
api
.
CustomizedProfile
)
(
string
,
error
)
{
if
len
(
memoList
)
==
0
{
return
""
,
nil
}
feed
:=
&
feeds
.
Feed
{
Title
:
profile
.
Name
,
Link
:
&
feeds
.
Link
{
Href
:
baseURL
},
Description
:
profile
.
Description
,
Created
:
time
.
Now
(),
}
feed
.
Items
=
make
([]
*
feeds
.
Item
,
len
(
memoList
))
for
i
,
memo
:=
range
memoList
{
var
useTitle
=
strings
.
HasPrefix
(
memo
.
Content
,
"# "
)
var
title
string
if
useTitle
{
title
=
strings
.
Split
(
memo
.
Content
,
"
\n
"
)[
0
][
2
:
]
}
else
{
title
=
memo
.
Creator
.
Username
+
"-memos-"
+
strconv
.
Itoa
(
memo
.
ID
)
}
var
description
string
if
useTitle
{
var
firstLineEnd
=
strings
.
Index
(
memo
.
Content
,
"
\n
"
)
description
=
memo
.
Content
[
firstLineEnd
+
1
:
]
}
else
{
description
=
memo
.
Content
}
feed
.
Items
[
i
]
=
&
feeds
.
Item
{
Title
:
title
,
Link
:
&
feeds
.
Link
{
Href
:
baseURL
+
"/m/"
+
strconv
.
Itoa
(
memo
.
ID
)},
Description
:
description
,
Created
:
time
.
Unix
(
memo
.
CreatedTs
,
0
),
}
}
rss
,
err
:=
feed
.
ToRss
()
if
err
!=
nil
{
return
""
,
err
}
rssPrefix
:=
`<?xml version="1.0" encoding="UTF-8"?>`
return
rss
[
len
(
rssPrefix
)
:
],
nil
}
func
(
s
*
Server
)
registerRSSRoutes
(
g
*
echo
.
Group
)
{
func
(
s
*
Server
)
registerRSSRoutes
(
g
*
echo
.
Group
)
{
g
.
GET
(
"/explore/rss.xml"
,
func
(
c
echo
.
Context
)
error
{
ctx
:=
c
.
Request
()
.
Context
()
systemCustomizedProfile
,
err
:=
getSystemCustomizedProfile
(
ctx
,
s
)
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to get system customized profile"
)
.
SetInternal
(
err
)
}
normalStatus
:=
api
.
Normal
memoFind
:=
api
.
MemoFind
{
RowStatus
:
&
normalStatus
,
VisibilityList
:
[]
api
.
Visibility
{
api
.
Public
,
},
}
memoList
,
err
:=
s
.
Store
.
FindMemoList
(
ctx
,
&
memoFind
)
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to find memo list"
)
.
SetInternal
(
err
)
}
baseURL
:=
c
.
Scheme
()
+
"://"
+
c
.
Request
()
.
Host
rss
,
err
:=
generateRSSFromMemoList
(
memoList
,
baseURL
,
&
systemCustomizedProfile
)
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to generate rss"
)
.
SetInternal
(
err
)
}
return
c
.
XMLBlob
(
http
.
StatusOK
,
[]
byte
(
rss
))
})
g
.
GET
(
"/u/:id/rss.xml"
,
func
(
c
echo
.
Context
)
error
{
g
.
GET
(
"/u/:id/rss.xml"
,
func
(
c
echo
.
Context
)
error
{
ctx
:=
c
.
Request
()
.
Context
()
ctx
:=
c
.
Request
()
.
Context
()
systemCustomizedProfile
,
err
:=
getSystemCustomizedProfile
(
ctx
,
s
)
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to get system customized profile"
)
.
SetInternal
(
err
)
}
id
,
err
:=
strconv
.
Atoi
(
c
.
Param
(
"id"
))
id
,
err
:=
strconv
.
Atoi
(
c
.
Param
(
"id"
))
if
err
!=
nil
{
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusBadRequest
,
"User id is not a number"
)
.
SetInternal
(
err
)
return
echo
.
NewHTTPError
(
http
.
StatusBadRequest
,
"User id is not a number"
)
.
SetInternal
(
err
)
...
@@ -32,41 +119,66 @@ func (s *Server) registerRSSRoutes(g *echo.Group) {
...
@@ -32,41 +119,66 @@ func (s *Server) registerRSSRoutes(g *echo.Group) {
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to find memo list"
)
.
SetInternal
(
err
)
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to find memo list"
)
.
SetInternal
(
err
)
}
}
userFind
:=
api
.
UserFind
{
baseURL
:=
c
.
Scheme
()
+
"://"
+
c
.
Request
()
.
Host
ID
:
&
id
,
}
rss
,
err
:=
generateRSSFromMemoList
(
memoList
,
baseURL
,
&
systemCustomizedProfile
)
user
,
err
:=
s
.
Store
.
FindUser
(
ctx
,
&
userFind
)
if
err
!=
nil
{
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to
find user
"
)
.
SetInternal
(
err
)
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to
generate rss
"
)
.
SetInternal
(
err
)
}
}
baseURL
:=
c
.
Scheme
()
+
"://"
+
c
.
Request
()
.
Host
return
c
.
XMLBlob
(
http
.
StatusOK
,
[]
byte
(
rss
))
})
}
feed
:=
&
feeds
.
Feed
{
func
getSystemCustomizedProfile
(
ctx
context
.
Context
,
s
*
Server
)
(
api
.
CustomizedProfile
,
error
)
{
Title
:
"Memos"
,
systemStatus
:=
api
.
SystemStatus
{
Link
:
&
feeds
.
Link
{
Href
:
baseURL
},
CustomizedProfile
:
api
.
CustomizedProfile
{
Description
:
"Memos"
,
Name
:
"memos"
,
Author
:
&
feeds
.
Author
{
Name
:
user
.
Username
},
LogoURL
:
""
,
Created
:
time
.
Now
(),
Description
:
""
,
Locale
:
"en"
,
Appearance
:
"system"
,
ExternalURL
:
""
,
},
}
}
feed
.
Items
=
make
([]
*
feeds
.
Item
,
len
(
memoList
))
systemSettingList
,
err
:=
s
.
Store
.
FindSystemSettingList
(
ctx
,
&
api
.
SystemSettingFind
{})
for
i
,
memo
:=
range
memoList
{
if
err
!=
nil
{
feed
.
Items
[
i
]
=
&
feeds
.
Item
{
return
api
.
CustomizedProfile
{},
err
Title
:
user
.
Username
+
"-memos-"
+
strconv
.
Itoa
(
memo
.
ID
),
Link
:
&
feeds
.
Link
{
Href
:
baseURL
+
"/m/"
+
strconv
.
Itoa
(
memo
.
ID
)},
Description
:
memo
.
Content
,
Created
:
time
.
Unix
(
memo
.
CreatedTs
,
0
),
}
}
for
_
,
systemSetting
:=
range
systemSettingList
{
if
systemSetting
.
Name
==
api
.
SystemSettingServerID
||
systemSetting
.
Name
==
api
.
SystemSettingSecretSessionName
{
continue
}
}
rss
,
err
:=
feed
.
ToRss
()
var
value
interface
{}
err
:=
json
.
Unmarshal
([]
byte
(
systemSetting
.
Value
),
&
value
)
if
err
!=
nil
{
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to generate rss"
)
.
SetInternal
(
err
)
return
api
.
CustomizedProfile
{},
err
}
}
rssPrefix
:=
`<?xml version="1.0" encoding="UTF-8"?>`
if
systemSetting
.
Name
==
api
.
SystemSettingCustomizedProfileName
{
valueMap
:=
value
.
(
map
[
string
]
interface
{})
return
c
.
XMLBlob
(
http
.
StatusOK
,
[]
byte
(
rss
[
len
(
rssPrefix
)
:
]))
systemStatus
.
CustomizedProfile
=
api
.
CustomizedProfile
{}
})
if
v
:=
valueMap
[
"name"
];
v
!=
nil
{
systemStatus
.
CustomizedProfile
.
Name
=
v
.
(
string
)
}
if
v
:=
valueMap
[
"logoUrl"
];
v
!=
nil
{
systemStatus
.
CustomizedProfile
.
LogoURL
=
v
.
(
string
)
}
if
v
:=
valueMap
[
"description"
];
v
!=
nil
{
systemStatus
.
CustomizedProfile
.
Description
=
v
.
(
string
)
}
if
v
:=
valueMap
[
"locale"
];
v
!=
nil
{
systemStatus
.
CustomizedProfile
.
Locale
=
v
.
(
string
)
}
if
v
:=
valueMap
[
"appearance"
];
v
!=
nil
{
systemStatus
.
CustomizedProfile
.
Appearance
=
v
.
(
string
)
}
if
v
:=
valueMap
[
"externalUrl"
];
v
!=
nil
{
systemStatus
.
CustomizedProfile
.
ExternalURL
=
v
.
(
string
)
}
}
}
return
systemStatus
.
CustomizedProfile
,
nil
}
}
web/src/components/MemosHeader.tsx
View file @
168c4f69
import
{
useCallback
,
useEffect
,
useState
}
from
"react"
;
import
{
useCallback
,
useEffect
,
useState
}
from
"react"
;
import
{
useLocationStore
,
useMemoStore
,
useShortcutStore
}
from
"../store/module"
;
import
{
useLocationStore
,
useMemoStore
,
useShortcutStore
,
useUserStore
}
from
"../store/module"
;
import
Icon
from
"./Icon"
;
import
Icon
from
"./Icon"
;
import
SearchBar
from
"./SearchBar"
;
import
SearchBar
from
"./SearchBar"
;
import
{
toggleSidebar
}
from
"./Sidebar"
;
import
{
toggleSidebar
}
from
"./Sidebar"
;
...
@@ -11,6 +11,8 @@ const MemosHeader = () => {
...
@@ -11,6 +11,8 @@ const MemosHeader = () => {
const
locationStore
=
useLocationStore
();
const
locationStore
=
useLocationStore
();
const
memoStore
=
useMemoStore
();
const
memoStore
=
useMemoStore
();
const
shortcutStore
=
useShortcutStore
();
const
shortcutStore
=
useShortcutStore
();
const
userStore
=
useUserStore
();
const
user
=
userStore
.
state
.
user
;
const
query
=
locationStore
.
state
.
query
;
const
query
=
locationStore
.
state
.
query
;
const
shortcuts
=
shortcutStore
.
state
.
shortcuts
;
const
shortcuts
=
shortcutStore
.
state
.
shortcuts
;
const
[
titleText
,
setTitleText
]
=
useState
(
"MEMOS"
);
const
[
titleText
,
setTitleText
]
=
useState
(
"MEMOS"
);
...
@@ -46,6 +48,11 @@ const MemosHeader = () => {
...
@@ -46,6 +48,11 @@ const MemosHeader = () => {
<
span
className=
"title-text"
onClick=
{
handleTitleTextClick
}
>
<
span
className=
"title-text"
onClick=
{
handleTitleTextClick
}
>
{
titleText
}
{
titleText
}
</
span
>
</
span
>
{
user
&&
(
<
a
className=
"dark:text-white"
href=
{
"/u/"
+
user
.
id
+
"/rss.xml"
}
target=
"_blank"
rel=
"noreferrer"
>
<
Icon
.
Rss
/>
</
a
>
)
}
</
div
>
</
div
>
<
SearchBar
/>
<
SearchBar
/>
</
div
>
</
div
>
...
...
web/src/pages/Explore.tsx
View file @
168c4f69
...
@@ -9,6 +9,7 @@ import toastHelper from "../components/Toast";
...
@@ -9,6 +9,7 @@ import toastHelper from "../components/Toast";
import
MemoContent
from
"../components/MemoContent"
;
import
MemoContent
from
"../components/MemoContent"
;
import
MemoResources
from
"../components/MemoResources"
;
import
MemoResources
from
"../components/MemoResources"
;
import
MemoFilter
from
"../components/MemoFilter"
;
import
MemoFilter
from
"../components/MemoFilter"
;
import
Icon
from
"../components/Icon"
;
import
{
TAG_REG
}
from
"../labs/marked/parser"
;
import
{
TAG_REG
}
from
"../labs/marked/parser"
;
import
"../less/explore.less"
;
import
"../less/explore.less"
;
...
@@ -115,6 +116,9 @@ const Explore = () => {
...
@@ -115,6 +116,9 @@ const Explore = () => {
<
div
className=
"title-container"
>
<
div
className=
"title-container"
>
<
img
className=
"logo-img"
src=
{
customizedProfile
.
logoUrl
}
alt=
""
/>
<
img
className=
"logo-img"
src=
{
customizedProfile
.
logoUrl
}
alt=
""
/>
<
span
className=
"title-text"
>
{
customizedProfile
.
name
}
</
span
>
<
span
className=
"title-text"
>
{
customizedProfile
.
name
}
</
span
>
<
a
className=
"dark:text-white ml-2"
href=
"/explore/rss.xml"
target=
"_blank"
rel=
"noreferrer"
>
<
Icon
.
Rss
/>
</
a
>
</
div
>
</
div
>
<
div
className=
"action-button-container"
>
<
div
className=
"action-button-container"
>
{
!
loadingState
.
isLoading
&&
user
?
(
{
!
loadingState
.
isLoading
&&
user
?
(
...
...
web/vite.config.ts
View file @
168c4f69
...
@@ -21,6 +21,10 @@ export default defineConfig({
...
@@ -21,6 +21,10 @@ export default defineConfig({
target
:
"http://localhost:8081/"
,
target
:
"http://localhost:8081/"
,
changeOrigin
:
true
,
changeOrigin
:
true
,
},
},
"/explore/rss.xml"
:
{
target
:
"http://localhost:8081/"
,
changeOrigin
:
true
,
},
},
},
},
},
resolve
:
{
resolve
:
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment