add atom, rss and json feed support
This commit is contained in:
parent
2a95341a99
commit
736bc7c4aa
@ -5,6 +5,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -133,75 +134,7 @@ func GetPost(c *fiber.Ctx) error{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFeed(c *fiber.Ctx) error{
|
|
||||||
var posts []Post = []Post{}
|
|
||||||
rows, err := DB.Query("SELECT * FROM posts")
|
|
||||||
if util.ErrorCheck(err, c) {
|
|
||||||
return util.ErrServer(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var post Post
|
|
||||||
err := PostFromRow(&post, rows)
|
|
||||||
|
|
||||||
if util.ErrorCheck(err, c) {
|
|
||||||
return util.ErrServer(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
if post.Public == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
posts = append(posts, post)
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
|
|
||||||
|
|
||||||
blogurl, err := url.JoinPath(os.Getenv("URL"), "/blog")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to create the blog URL: %s\n", err.Error())
|
|
||||||
return c.JSON(fiber.Map{"error": "Server error"})
|
|
||||||
}
|
|
||||||
|
|
||||||
feed := &feeds.Feed{
|
|
||||||
Title: "[ngn] | blog",
|
|
||||||
Link: &feeds.Link{Href: blogurl},
|
|
||||||
Description: "ngn's personal blog",
|
|
||||||
Author: &feeds.Author{Name: "ngn", Email: "ngn@ngn.tf"},
|
|
||||||
Created: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
feed.Items = []*feeds.Item{}
|
|
||||||
for _, p := range posts {
|
|
||||||
purl, err := url.JoinPath(blogurl, p.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to create URL for '%s': %s\n", p.ID, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed, err := time.Parse("02/01/06", p.Date)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to parse time for '%s': %s\n", p.ID, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
feed.Items = append(feed.Items, &feeds.Item{
|
|
||||||
Title: p.Title,
|
|
||||||
Link: &feeds.Link{Href: purl},
|
|
||||||
Author: &feeds.Author{Name: p.Author},
|
|
||||||
Created: parsed,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
atom, err := feed.ToAtom()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to create atom feed: %s", err.Error())
|
|
||||||
return c.JSON(fiber.Map{"error": "Server error"})
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("Content-Type", "application/atom+xml")
|
|
||||||
return c.Send([]byte(atom))
|
|
||||||
}
|
|
||||||
|
|
||||||
func SumPost(c *fiber.Ctx) error{
|
func SumPost(c *fiber.Ctx) error{
|
||||||
var posts []Post = []Post{}
|
var posts []Post = []Post{}
|
||||||
@ -235,3 +168,108 @@ func SumPost(c *fiber.Ctx) error{
|
|||||||
"result": posts,
|
"result": posts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFeed() (*feeds.Feed, error){
|
||||||
|
var posts []Post = []Post{}
|
||||||
|
rows, err := DB.Query("SELECT * FROM posts")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var post Post
|
||||||
|
err := PostFromRow(&post, rows)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if post.Public == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
posts = append(posts, post)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
|
||||||
|
|
||||||
|
blogurl, err := url.JoinPath(os.Getenv("URL"), "/blog")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create the blog URL: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
feed := &feeds.Feed{
|
||||||
|
Title: "[ngn.tf] | blog",
|
||||||
|
Link: &feeds.Link{Href: blogurl},
|
||||||
|
Description: "ngn's personal blog",
|
||||||
|
Author: &feeds.Author{Name: "ngn", Email: "ngn@ngn.tf"},
|
||||||
|
Created: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
feed.Items = []*feeds.Item{}
|
||||||
|
for _, p := range posts {
|
||||||
|
purl, err := url.JoinPath(blogurl, p.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create URL for '%s': %s\n", p.ID, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := time.Parse("02/01/06", p.Date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse time for '%s': %s\n", p.ID, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
feed.Items = append(feed.Items, &feeds.Item{
|
||||||
|
Id: p.ID,
|
||||||
|
Title: p.Title,
|
||||||
|
Link: &feeds.Link{Href: purl},
|
||||||
|
Author: &feeds.Author{Name: p.Author},
|
||||||
|
Created: parsed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return feed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAtomFeed(c *fiber.Ctx) error {
|
||||||
|
feed, err := GetFeed()
|
||||||
|
if util.ErrorCheck(err, c){
|
||||||
|
return util.ErrServer(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
atom, err := feed.ToAtom()
|
||||||
|
if util.ErrorCheck(err, c){
|
||||||
|
return util.ErrServer(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("Content-Type", "application/atom+xml")
|
||||||
|
return c.Send([]byte(atom))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRSSFeed(c *fiber.Ctx) error {
|
||||||
|
feed, err := GetFeed()
|
||||||
|
if util.ErrorCheck(err, c){
|
||||||
|
return util.ErrServer(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
rss, err := feed.ToRss()
|
||||||
|
if util.ErrorCheck(err, c){
|
||||||
|
return util.ErrServer(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("Content-Type", "application/rss+xml")
|
||||||
|
return c.Send([]byte(rss))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetJSONFeed(c *fiber.Ctx) error {
|
||||||
|
feed, err := GetFeed()
|
||||||
|
if util.ErrorCheck(err, c){
|
||||||
|
return util.ErrServer(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := feed.ToJSON()
|
||||||
|
if util.ErrorCheck(err, c){
|
||||||
|
return util.ErrServer(c)
|
||||||
|
}
|
||||||
|
c.Set("Content-Type", "application/feed+json")
|
||||||
|
return c.Send([]byte(json))
|
||||||
|
}
|
||||||
|
@ -19,7 +19,9 @@ func Setup(app *fiber.App, db *sql.DB){
|
|||||||
})
|
})
|
||||||
|
|
||||||
// blog routes
|
// blog routes
|
||||||
app.Get("/blog/feed", GetFeed)
|
app.Get("/blog/feed.atom", GetAtomFeed)
|
||||||
|
app.Get("/blog/feed.rss", GetRSSFeed)
|
||||||
|
app.Get("/blog/feed.json", GetJSONFeed)
|
||||||
app.Get("/blog/sum", SumPost)
|
app.Get("/blog/sum", SumPost)
|
||||||
app.Get("/blog/get", GetPost)
|
app.Get("/blog/get", GetPost)
|
||||||
app.Get("/blog/vote/set", VoteSet)
|
app.Get("/blog/vote/set", VoteSet)
|
||||||
|
4
app/package-lock.json
generated
4
app/package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "website",
|
"name": "website",
|
||||||
"version": "4.5.0",
|
"version": "4.6.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "website",
|
"name": "website",
|
||||||
"version": "4.5.0",
|
"version": "4.6.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/dompurify": "^3.0.2",
|
"@types/dompurify": "^3.0.2",
|
||||||
"dompurify": "^3.1.0",
|
"dompurify": "^3.1.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "website",
|
"name": "website",
|
||||||
"version": "4.6.0",
|
"version": "4.8.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
<p>v4.6</p>
|
<p>v4.8</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -19,6 +19,18 @@
|
|||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<div class="feed-list">
|
||||||
|
<a href="{import.meta.env.VITE_API_URL_DEV+'/blog/feed.rss'}">
|
||||||
|
<c><i class="nf nf-fa-rss_square"></i></c> <p>RSS</p>
|
||||||
|
</a>
|
||||||
|
<a href="{import.meta.env.VITE_API_URL_DEV+'/blog/feed.atom'}">
|
||||||
|
<c><i class="nf nf-fae-atom"></i></c> <p>Atom</p>
|
||||||
|
</a>
|
||||||
|
<a href="{import.meta.env.VITE_API_URL_DEV+'/blog/feed.json'}">
|
||||||
|
<c><i class="nf nf-seti-json"></i></c> <p>JSON</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="post-list">
|
||||||
{#each posts as post}
|
{#each posts as post}
|
||||||
<CardLink url="/blog/{post.id}" title="{post.title}">
|
<CardLink url="/blog/{post.id}" title="{post.title}">
|
||||||
<p>{post.author} | {post.date}</p>
|
<p>{post.author} | {post.date}</p>
|
||||||
@ -26,21 +38,62 @@
|
|||||||
{post.content}...
|
{post.content}...
|
||||||
</CardLink>
|
</CardLink>
|
||||||
{/each}
|
{/each}
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
main{
|
.post-list{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 35px;
|
gap: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
padding: 15%;
|
padding: 15%;
|
||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feed-list{
|
||||||
|
text-align: right;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed-list a {
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 18px 10px 18px;
|
||||||
|
background: var(--dark-three);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 900;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
|
||||||
|
transition: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed-list a:hover {
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed-list a i{
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1316px) {
|
@media only screen and (max-width: 1316px) {
|
||||||
main {
|
main {
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
<meta content="{data.content.substring(0, 100)}..." property="og:description" />
|
<meta content="{data.content.substring(0, 100)}..." property="og:description" />
|
||||||
<meta content="https://ngn.tf" property="og:url" />
|
<meta content="https://ngn.tf" property="og:url" />
|
||||||
<meta content="#000000" data-react-helmet="true" name="theme-color" />
|
<meta content="#000000" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="https://files.ngn.tf/markdown.css" rel="stylesheet">
|
<link href="/markdown.css" rel="stylesheet">
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Header>
|
<Header>
|
||||||
@ -123,6 +123,8 @@
|
|||||||
{@html sanitized}
|
{@html sanitized}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
p {
|
p {
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
|
1023
app/static/markdown.css
Normal file
1023
app/static/markdown.css
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user