feat: add open graph metadata field

This commit is contained in:
Steven 2023-07-21 21:27:05 +08:00
parent 014dd7d660
commit a91997683b
7 changed files with 135 additions and 36 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"github.com/boojack/slash/store"
"github.com/labstack/echo/v4"
@ -42,11 +43,36 @@ func (s *APIV1Service) registerRedirectorRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to create activity, err: %s", err)).SetInternal(err)
}
if isValidURLString(shortcut.Link) {
return redirectToShortcut(c, shortcut)
})
}
func redirectToShortcut(c echo.Context, shortcut *store.Shortcut) error {
isValidURL := isValidURLString(shortcut.Link)
if shortcut.OpenGraphMetadata == nil {
if isValidURL {
return c.Redirect(http.StatusSeeOther, shortcut.Link)
}
return c.String(http.StatusOK, shortcut.Link)
})
}
htmlTemplate := `<html><head>%s</head><body>%s</body></html>`
metadataList := []string{
fmt.Sprintf(`<meta property="og:title" content="%s" />`, shortcut.OpenGraphMetadata.Title),
fmt.Sprintf(`<meta property="og:description" content="%s" />`, shortcut.OpenGraphMetadata.Description),
fmt.Sprintf(`<meta property="og:image" content="%s" />`, shortcut.OpenGraphMetadata.Image),
}
if isValidURL {
metadataList = append(metadataList, fmt.Sprintf(`<meta property="og:url" content="%s" />`, shortcut.Link))
}
body := ""
if isValidURL {
body = fmt.Sprintf(`<script>window.location.href = "%s";</script>`, shortcut.Link)
} else {
body = shortcut.Link
}
htmlString := fmt.Sprintf(htmlTemplate, strings.Join(metadataList, ""), body)
return c.HTML(http.StatusOK, htmlString)
}
func (s *APIV1Service) createShortcutViewActivity(c echo.Context, shortcut *store.Shortcut) error {

View File

@ -30,6 +30,12 @@ func (v Visibility) String() string {
return string(v)
}
type OpenGraphMetadata struct {
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
}
type Shortcut struct {
ID int `json:"id"`
@ -41,29 +47,32 @@ type Shortcut struct {
RowStatus RowStatus `json:"rowStatus"`
// Domain specific fields
Name string `json:"name"`
Link string `json:"link"`
Description string `json:"description"`
Visibility Visibility `json:"visibility"`
Tags []string `json:"tags"`
View int `json:"view"`
Name string `json:"name"`
Link string `json:"link"`
Description string `json:"description"`
Visibility Visibility `json:"visibility"`
Tags []string `json:"tags"`
View int `json:"view"`
OpenGraphMetadata *OpenGraphMetadata `json:"openGraphMetadata"`
}
type CreateShortcutRequest struct {
Name string `json:"name"`
Link string `json:"link"`
Description string `json:"description"`
Visibility Visibility `json:"visibility"`
Tags []string `json:"tags"`
Name string `json:"name"`
Link string `json:"link"`
Description string `json:"description"`
Visibility Visibility `json:"visibility"`
Tags []string `json:"tags"`
OpenGraphMetadata *OpenGraphMetadata `json:"openGraphMetadata"`
}
type PatchShortcutRequest struct {
RowStatus *RowStatus `json:"rowStatus"`
Name *string `json:"name"`
Link *string `json:"link"`
Description *string `json:"description"`
Visibility *Visibility `json:"visibility"`
Tags []string `json:"tags"`
RowStatus *RowStatus `json:"rowStatus"`
Name *string `json:"name"`
Link *string `json:"link"`
Description *string `json:"description"`
Visibility *Visibility `json:"visibility"`
Tags []string `json:"tags"`
OpenGraphMetadata *OpenGraphMetadata `json:"openGraphMetadata"`
}
func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) {
@ -85,6 +94,11 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) {
Description: create.Description,
Visibility: store.Visibility(create.Visibility.String()),
Tag: strings.Join(create.Tags, " "),
OpenGraphMetadata: &store.OpenGraphMetadata{
Title: create.OpenGraphMetadata.Title,
Description: create.OpenGraphMetadata.Description,
Image: create.OpenGraphMetadata.Image,
},
})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to create shortcut, err: %s", err)).SetInternal(err)
@ -156,6 +170,13 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) {
tag := strings.Join(patch.Tags, " ")
shortcutUpdate.Tag = &tag
}
if patch.OpenGraphMetadata != nil {
shortcutUpdate.OpenGraphMetadata = &store.OpenGraphMetadata{
Title: patch.OpenGraphMetadata.Title,
Description: patch.OpenGraphMetadata.Description,
Image: patch.OpenGraphMetadata.Image,
}
}
shortcut, err = s.Store.UpdateShortcut(ctx, shortcutUpdate)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to patch shortcut, err: %s", err)).SetInternal(err)
@ -315,6 +336,11 @@ func convertShortcutFromStore(shortcut *store.Shortcut) *Shortcut {
Visibility: Visibility(shortcut.Visibility),
RowStatus: RowStatus(shortcut.RowStatus),
Tags: tags,
OpenGraphMetadata: &OpenGraphMetadata{
Title: shortcut.OpenGraphMetadata.Title,
Description: shortcut.OpenGraphMetadata.Description,
Image: shortcut.OpenGraphMetadata.Image,
},
}
}

View File

@ -43,7 +43,8 @@ CREATE TABLE shortcut (
link TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
visibility TEXT NOT NULL CHECK (visibility IN ('PRIVATE', 'WORKSPACE', 'PUBLIC')) DEFAULT 'PRIVATE',
tag TEXT NOT NULL DEFAULT ''
tag TEXT NOT NULL DEFAULT '',
og_metadata TEXT NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_shortcut_name ON shortcut(name);

View File

@ -0,0 +1 @@
ALTER TABLE shortcut ADD COLUMN og_metadata TEXT NOT NULL DEFAULT '{}';

View File

@ -43,7 +43,8 @@ CREATE TABLE shortcut (
link TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
visibility TEXT NOT NULL CHECK (visibility IN ('PRIVATE', 'WORKSPACE', 'PUBLIC')) DEFAULT 'PRIVATE',
tag TEXT NOT NULL DEFAULT ''
tag TEXT NOT NULL DEFAULT '',
og_metadata TEXT NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_shortcut_name ON shortcut(name);

View File

@ -38,7 +38,8 @@ INSERT INTO
`creator_id`,
`name`,
`link`,
`visibility`
`visibility`,
`og_metadata`
)
VALUES
(
@ -46,7 +47,8 @@ VALUES
101,
'schema-change',
'https://www.bytebase.com/blog/how-to-handle-database-schema-change/#what-is-a-database-schema-change',
'PUBLIC'
'PUBLIC',
'{"title":"How to Handle Database Migration / Schema Change?","description":"A database schema is the structure of a database, which describes the relationships between the different tables and fields in the database. A database schema change, also known as schema migration, or simply migration refers to any alteration to this structure, such as adding a new table, modifying the data type of a field, or changing the relationships between tables.","image":"https://www.bytebase.com/_next/image/?url=%2Fcontent%2Fblog%2Fhow-to-handle-database-schema-change%2Fchange.webp\u0026w=2048\u0026q=75"}'
);
INSERT INTO

View File

@ -3,6 +3,7 @@ package store
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"strings"
)
@ -31,6 +32,12 @@ func (e Visibility) String() string {
return "PRIVATE"
}
type OpenGraphMetadata struct {
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
}
type Shortcut struct {
ID int
@ -41,22 +48,24 @@ type Shortcut struct {
RowStatus RowStatus
// Domain specific fields
Name string
Link string
Description string
Visibility Visibility
Tag string
Name string
Link string
Description string
Visibility Visibility
Tag string
OpenGraphMetadata *OpenGraphMetadata
}
type UpdateShortcut struct {
ID int
RowStatus *RowStatus
Name *string
Link *string
Description *string
Visibility *Visibility
Tag *string
RowStatus *RowStatus
Name *string
Link *string
Description *string
Visibility *Visibility
Tag *string
OpenGraphMetadata *OpenGraphMetadata
}
type FindShortcut struct {
@ -76,6 +85,15 @@ func (s *Store) CreateShortcut(ctx context.Context, create *Shortcut) (*Shortcut
set := []string{"creator_id", "name", "link", "description", "visibility", "tag"}
args := []any{create.CreatorID, create.Name, create.Link, create.Description, create.Visibility, create.Tag}
placeholder := []string{"?", "?", "?", "?", "?", "?"}
if create.OpenGraphMetadata != nil {
set = append(set, "open_graph_metadata")
openGraphMetadataBytes, err := json.Marshal(create.OpenGraphMetadata)
if err != nil {
return nil, err
}
args = append(args, string(openGraphMetadataBytes))
placeholder = append(placeholder, "?")
}
stmt := `
INSERT INTO shortcut (
@ -116,6 +134,13 @@ func (s *Store) UpdateShortcut(ctx context.Context, update *UpdateShortcut) (*Sh
if update.Tag != nil {
set, args = append(set, "tag = ?"), append(args, *update.Tag)
}
if update.OpenGraphMetadata != nil {
openGraphMetadataBytes, err := json.Marshal(update.OpenGraphMetadata)
if err != nil {
return nil, err
}
set, args = append(set, "og_metadata = ?"), append(args, string(openGraphMetadataBytes))
}
if len(set) == 0 {
return nil, fmt.Errorf("no update specified")
}
@ -127,9 +152,10 @@ func (s *Store) UpdateShortcut(ctx context.Context, update *UpdateShortcut) (*Sh
` + strings.Join(set, ", ") + `
WHERE
id = ?
RETURNING id, creator_id, created_ts, updated_ts, row_status, name, link, description, visibility, tag
RETURNING id, creator_id, created_ts, updated_ts, row_status, name, link, description, visibility, tag, og_metadata
`
shortcut := &Shortcut{}
openGraphMetadataString := ""
if err := s.db.QueryRowContext(ctx, stmt, args...).Scan(
&shortcut.ID,
&shortcut.CreatorID,
@ -141,9 +167,16 @@ func (s *Store) UpdateShortcut(ctx context.Context, update *UpdateShortcut) (*Sh
&shortcut.Description,
&shortcut.Visibility,
&shortcut.Tag,
&openGraphMetadataString,
); err != nil {
return nil, err
}
if openGraphMetadataString != "" {
shortcut.OpenGraphMetadata = &OpenGraphMetadata{}
if err := json.Unmarshal([]byte(openGraphMetadataString), shortcut.OpenGraphMetadata); err != nil {
return nil, err
}
}
s.shortcutCache.Store(shortcut.ID, shortcut)
return shortcut, nil
@ -186,7 +219,8 @@ func (s *Store) ListShortcuts(ctx context.Context, find *FindShortcut) ([]*Short
link,
description,
visibility,
tag
tag,
og_metadata
FROM shortcut
WHERE `+strings.Join(where, " AND ")+`
ORDER BY created_ts DESC`,
@ -200,6 +234,7 @@ func (s *Store) ListShortcuts(ctx context.Context, find *FindShortcut) ([]*Short
list := make([]*Shortcut, 0)
for rows.Next() {
shortcut := &Shortcut{}
openGraphMetadataString := ""
if err := rows.Scan(
&shortcut.ID,
&shortcut.CreatorID,
@ -211,9 +246,16 @@ func (s *Store) ListShortcuts(ctx context.Context, find *FindShortcut) ([]*Short
&shortcut.Description,
&shortcut.Visibility,
&shortcut.Tag,
&openGraphMetadataString,
); err != nil {
return nil, err
}
if openGraphMetadataString != "" {
shortcut.OpenGraphMetadata = &OpenGraphMetadata{}
if err := json.Unmarshal([]byte(openGraphMetadataString), shortcut.OpenGraphMetadata); err != nil {
return nil, err
}
}
list = append(list, shortcut)
}