Merge branch 'master' into fmartingr/issue657

This commit is contained in:
Felipe Martin 2024-08-27 18:49:28 +02:00 committed by GitHub
commit a91515ca2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 307 additions and 108 deletions

View file

@ -1,6 +1,11 @@
before:
hooks:
- go mod tidy
git:
ignore_tags:
- "{{ if not .IsNightly }}*-rc*{{ end }}"
builds:
- binary: shiori
env:
@ -26,6 +31,7 @@ builds:
goarch: arm
- goos: windows
goarch: arm64
archives:
- id: shiori
name_template: >-
@ -47,8 +53,10 @@ archives:
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
groups:
@ -79,5 +87,6 @@ changelog:
exclude:
- "^deps:"
- "^chore\\(deps\\):"
release:
prerelease: auto

View file

@ -520,6 +520,9 @@ const docTemplate = `{
"description": "TODO: migrate outside the DTO",
"type": "boolean"
},
"createdAt": {
"type": "string"
},
"excerpt": {
"type": "string"
},
@ -541,7 +544,7 @@ const docTemplate = `{
"imageURL": {
"type": "string"
},
"modified": {
"modifiedAt": {
"type": "string"
},
"public": {

View file

@ -509,6 +509,9 @@
"description": "TODO: migrate outside the DTO",
"type": "boolean"
},
"createdAt": {
"type": "string"
},
"excerpt": {
"type": "string"
},
@ -530,7 +533,7 @@
"imageURL": {
"type": "string"
},
"modified": {
"modifiedAt": {
"type": "string"
},
"public": {

View file

@ -99,6 +99,8 @@ definitions:
create_ebook:
description: 'TODO: migrate outside the DTO'
type: boolean
createdAt:
type: string
excerpt:
type: string
hasArchive:
@ -113,7 +115,7 @@ definitions:
type: integer
imageURL:
type: string
modified:
modifiedAt:
type: string
public:
type: integer

View file

@ -62,7 +62,7 @@ func exportHandler(cmd *cobra.Command, args []string) {
for _, book := range bookmarks {
// Create Unix timestamp for bookmark
modifiedTime, err := time.Parse(model.DatabaseDateFormat, book.Modified)
modifiedTime, err := time.Parse(model.DatabaseDateFormat, book.ModifiedAt)
if err != nil {
modifiedTime = time.Now()
}

View file

@ -136,10 +136,10 @@ func importHandler(cmd *cobra.Command, args []string) {
// Add item to list
bookmark := model.BookmarkDTO{
URL: url,
Title: title,
Tags: tags,
Modified: modifiedDate.Format(model.DatabaseDateFormat),
URL: url,
Title: title,
Tags: tags,
ModifiedAt: modifiedDate.Format(model.DatabaseDateFormat),
}
mapURL[url] = struct{}{}

View file

@ -94,10 +94,10 @@ func pocketHandler(cmd *cobra.Command, args []string) {
// Add item to list
bookmark := model.BookmarkDTO{
URL: url,
Title: title,
Modified: modified.Format(model.DatabaseDateFormat),
Tags: tags,
URL: url,
Title: title,
ModifiedAt: modified.Format(model.DatabaseDateFormat),
Tags: tags,
}
mapURL[url] = struct{}{}

View file

@ -62,6 +62,12 @@ func GenerateEbook(deps *dependencies.Dependencies, req ProcessRequest, dstPath
ebook.SetTitle(book.Title)
ebook.SetAuthor(book.Author)
if deps.Domains.Storage.FileExists(bookmarkThumbnailPath) {
// TODO: Use `deps.Domains.Storage` to retrieve the file.
absoluteCoverPath := fp.Join(deps.Config.Storage.DataDir, bookmarkThumbnailPath)
coverPath, _ := ebook.AddImage(absoluteCoverPath, "cover.jpg")
ebook.SetCover(coverPath, "")
}
ebook.SetDescription(book.Excerpt)
_, err = ebook.AddSection(`<h1 style="text-align:center"> `+book.Title+` </h1>`+book.HTML+lastline, book.Title, "", "")
if err != nil {

View file

@ -120,6 +120,7 @@ func ProcessBookmark(deps *dependencies.Dependencies, req ProcessRequest) (book
}
book.HasContent = book.Content != ""
book.ModifiedAt = ""
}
// Save article image to local disk
@ -137,6 +138,7 @@ func ProcessBookmark(deps *dependencies.Dependencies, req ProcessRequest) (book
}
if err == nil {
book.ImageURL = fp.Join("/", "bookmark", strID, "thumb")
book.ModifiedAt = ""
break
}
}
@ -154,6 +156,7 @@ func ProcessBookmark(deps *dependencies.Dependencies, req ProcessRequest) (book
return book, true, errors.Wrap(err, "failed to create ebook")
}
book.HasEbook = true
book.ModifiedAt = ""
}
}
@ -186,6 +189,7 @@ func ProcessBookmark(deps *dependencies.Dependencies, req ProcessRequest) (book
}
book.HasArchive = true
book.ModifiedAt = ""
}
return book, false, nil

View file

@ -3,6 +3,7 @@ package database
import (
"context"
"testing"
"time"
"github.com/go-shiori/shiori/internal/model"
"github.com/stretchr/testify/assert"
@ -15,19 +16,21 @@ type testDatabaseFactory func(t *testing.T, ctx context.Context) (DB, error)
func testDatabase(t *testing.T, dbFactory testDatabaseFactory) {
tests := map[string]databaseTestCase{
// Bookmarks
"testBookmarkAutoIncrement": testBookmarkAutoIncrement,
"testCreateBookmark": testCreateBookmark,
"testCreateBookmarkWithContent": testCreateBookmarkWithContent,
"testCreateBookmarkTwice": testCreateBookmarkTwice,
"testCreateBookmarkWithTag": testCreateBookmarkWithTag,
"testCreateTwoDifferentBookmarks": testCreateTwoDifferentBookmarks,
"testUpdateBookmark": testUpdateBookmark,
"testUpdateBookmarkWithContent": testUpdateBookmarkWithContent,
"testGetBookmark": testGetBookmark,
"testGetBookmarkNotExistent": testGetBookmarkNotExistent,
"testGetBookmarks": testGetBookmarks,
"testGetBookmarksWithSQLCharacters": testGetBookmarksWithSQLCharacters,
"testGetBookmarksCount": testGetBookmarksCount,
"testBookmarkAutoIncrement": testBookmarkAutoIncrement,
"testCreateBookmark": testCreateBookmark,
"testCreateBookmarkWithContent": testCreateBookmarkWithContent,
"testCreateBookmarkTwice": testCreateBookmarkTwice,
"testCreateBookmarkWithTag": testCreateBookmarkWithTag,
"testCreateTwoDifferentBookmarks": testCreateTwoDifferentBookmarks,
"testUpdateBookmark": testUpdateBookmark,
"testUpdateBookmarkUpdatesModifiedTime": testUpdateBookmarkUpdatesModifiedTime,
"testGetBoomarksWithTimeFilters": testGetBoomarksWithTimeFilters,
"testUpdateBookmarkWithContent": testUpdateBookmarkWithContent,
"testGetBookmark": testGetBookmark,
"testGetBookmarkNotExistent": testGetBookmarkNotExistent,
"testGetBookmarks": testGetBookmarks,
"testGetBookmarksWithSQLCharacters": testGetBookmarksWithSQLCharacters,
"testGetBookmarksCount": testGetBookmarksCount,
// Tags
"testCreateTag": testCreateTag,
"testCreateTags": testCreateTags,
@ -500,3 +503,92 @@ func testListAccountsWithPassword(t *testing.T, db DB) {
require.NotEmpty(t, acc.Password)
}
}
// TODO: Consider using `t.Parallel()` once we have automated database tests spawning databases using testcontainers.
func testUpdateBookmarkUpdatesModifiedTime(t *testing.T, db DB) {
ctx := context.TODO()
book := model.BookmarkDTO{
URL: "https://github.com/go-shiori/shiori",
Title: "shiori",
}
resultBook, err := db.SaveBookmarks(ctx, true, book)
assert.NoError(t, err, "Save bookmarks must not fail")
updatedBook := resultBook[0]
updatedBook.Title = "modified"
updatedBook.ModifiedAt = ""
time.Sleep(1 * time.Second)
resultUpdatedBooks, err := db.SaveBookmarks(ctx, false, updatedBook)
assert.NoError(t, err, "Save bookmarks must not fail")
assert.NotEqual(t, resultBook[0].ModifiedAt, resultUpdatedBooks[0].ModifiedAt)
assert.Equal(t, resultBook[0].CreatedAt, resultUpdatedBooks[0].CreatedAt)
assert.Equal(t, resultBook[0].CreatedAt, resultBook[0].ModifiedAt)
assert.NoError(t, err, "Get bookmarks must not fail")
assert.Equal(t, updatedBook.Title, resultUpdatedBooks[0].Title, "Saved bookmark must have updated Title")
}
// TODO: Consider using `t.Parallel()` once we have automated database tests spawning databases using testcontainers.
func testGetBoomarksWithTimeFilters(t *testing.T, db DB) {
ctx := context.TODO()
book1 := model.BookmarkDTO{
URL: "https://github.com/go-shiori/shiori/one",
Title: "Added First but Modified Last",
}
book2 := model.BookmarkDTO{
URL: "https://github.com/go-shiori/shiori/second",
Title: "Added Last but Modified First",
}
// create two new bookmark
resultBook1, err := db.SaveBookmarks(ctx, true, book1)
assert.NoError(t, err, "Save bookmarks must not fail")
time.Sleep(1 * time.Second)
resultBook2, err := db.SaveBookmarks(ctx, true, book2)
assert.NoError(t, err, "Save bookmarks must not fail")
// update those bookmarks
updatedBook1 := resultBook1[0]
updatedBook1.Title = "Added First but Modified Last Updated Title"
updatedBook1.ModifiedAt = ""
updatedBook2 := resultBook2[0]
updatedBook2.Title = "Last Added but modified First Updated Title"
updatedBook2.ModifiedAt = ""
// modified bookmark2 first after one second modified bookmark1
resultUpdatedBook2, err := db.SaveBookmarks(ctx, false, updatedBook2)
assert.NoError(t, err, "Save bookmarks must not fail")
time.Sleep(1 * time.Second)
resultUpdatedBook1, err := db.SaveBookmarks(ctx, false, updatedBook1)
assert.NoError(t, err, "Save bookmarks must not fail")
// get diffrent filteter combination
booksOrderByLastAdded, err := db.GetBookmarks(ctx, GetBookmarksOptions{
IDs: []int{resultUpdatedBook1[0].ID, resultUpdatedBook2[0].ID},
OrderMethod: 1,
})
assert.NoError(t, err, "Get bookmarks must not fail")
booksOrderByLastModified, err := db.GetBookmarks(ctx, GetBookmarksOptions{
IDs: []int{resultUpdatedBook1[0].ID, resultUpdatedBook2[0].ID},
OrderMethod: 2,
})
assert.NoError(t, err, "Get bookmarks must not fail")
booksOrderById, err := db.GetBookmarks(ctx, GetBookmarksOptions{
IDs: []int{resultUpdatedBook1[0].ID, resultUpdatedBook2[0].ID},
OrderMethod: 0,
})
assert.NoError(t, err, "Get bookmarks must not fail")
// Check Last Added
assert.Equal(t, booksOrderByLastAdded[0].Title, updatedBook2.Title)
// Check Last Modified
assert.Equal(t, booksOrderByLastModified[0].Title, updatedBook1.Title)
// Second id should be 2 if order them by id
assert.Equal(t, booksOrderById[1].ID, 2)
}

View file

@ -0,0 +1 @@
ALTER TABLE bookmark RENAME COLUMN modified to created_at;

View file

@ -0,0 +1,2 @@
ALTER TABLE bookmark
MODIFY created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;

View file

@ -0,0 +1,2 @@
ALTER TABLE bookmark
ADD COLUMN modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;

View file

@ -0,0 +1,3 @@
UPDATE bookmark
SET modified_at = COALESCE(created_at, CURRENT_TIMESTAMP)
WHERE created_at IS NOT NULL;

View file

@ -0,0 +1 @@
CREATE INDEX idx_created_at ON bookmark (created_at);

View file

@ -0,0 +1 @@
CREATE INDEX idx_modified_at ON bookmark (modified_at);

View file

@ -0,0 +1,16 @@
-- Rename "modified" column to "created_at"
ALTER TABLE bookmark
RENAME COLUMN modified to created_at;
-- Add the "modified_at" column to the bookmark table
ALTER TABLE bookmark
ADD COLUMN modified_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP;
-- Update the "modified_at" column with the value from the "created_at" column if it is not null
UPDATE bookmark
SET modified_at = COALESCE(created_at, CURRENT_TIMESTAMP)
WHERE created_at IS NOT NULL;
-- Index for "created_at" "modified_at""
CREATE INDEX idx_created_at ON bookmark(created_at);
CREATE INDEX idx_modified_at ON bookmark(modified_at);

View file

@ -0,0 +1,12 @@
ALTER TABLE bookmark
RENAME COLUMN modified to created_at;
ALTER TABLE bookmark
ADD COLUMN modified_at TEXT NULL;
UPDATE bookmark
SET modified_at = bookmark.created_at
WHERE created_at IS NOT NULL;
CREATE INDEX idx_created_at ON bookmark(created_at);
CREATE INDEX idx_modified_at ON bookmark(modified_at);

View file

@ -60,6 +60,12 @@ var mysqlMigrations = []migration{
return nil
}),
newFileMigration("0.7.0", "0.8.0", "mysql/0005_rename_to_created_at"),
newFileMigration("0.8.0", "0.8.1", "mysql/0006_change_created_at_settings"),
newFileMigration("0.8.1", "0.8.2", "mysql/0007_add_modified_at"),
newFileMigration("0.8.2", "0.8.3", "mysql/0008_set_modified_at_equal_created_at"),
newFileMigration("0.8.3", "0.8.4", "mysql/0009_index_for_created_at"),
newFileMigration("0.8.4", "0.8.5", "mysql/0010_index_for_modified_at"),
}
// MySQLDatabase is implementation of Database interface
@ -130,8 +136,8 @@ func (db *MySQLDatabase) SaveBookmarks(ctx context.Context, create bool, bookmar
if err := db.withTx(ctx, func(tx *sqlx.Tx) error {
// Prepare statement
stmtInsertBook, err := tx.Preparex(`INSERT INTO bookmark
(url, title, excerpt, author, public, content, html, modified)
VALUES(?, ?, ?, ?, ?, ?, ?, ?)`)
(url, title, excerpt, author, public, content, html, modified_at, created_at)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)`)
if err != nil {
return errors.WithStack(err)
}
@ -144,7 +150,7 @@ func (db *MySQLDatabase) SaveBookmarks(ctx context.Context, create bool, bookmar
public = ?,
content = ?,
html = ?,
modified = ?
modified_at = ?
WHERE id = ?`)
if err != nil {
return errors.WithStack(err)
@ -188,17 +194,18 @@ func (db *MySQLDatabase) SaveBookmarks(ctx context.Context, create bool, bookmar
}
// Set modified time
if book.Modified == "" {
book.Modified = modifiedTime
if book.ModifiedAt == "" {
book.ModifiedAt = modifiedTime
}
// Save bookmark
var err error
if create {
book.CreatedAt = modifiedTime
var res sql.Result
res, err = stmtInsertBook.ExecContext(ctx,
book.URL, book.Title, book.Excerpt, book.Author,
book.Public, book.Content, book.HTML, book.Modified)
book.Public, book.Content, book.HTML, book.ModifiedAt, book.CreatedAt)
if err != nil {
return errors.WithStack(err)
}
@ -210,7 +217,7 @@ func (db *MySQLDatabase) SaveBookmarks(ctx context.Context, create bool, bookmar
} else {
_, err = stmtUpdateBook.ExecContext(ctx,
book.URL, book.Title, book.Excerpt, book.Author,
book.Public, book.Content, book.HTML, book.Modified, book.ID)
book.Public, book.Content, book.HTML, book.ModifiedAt, book.ID)
}
if err != nil {
return errors.WithStack(err)
@ -284,7 +291,8 @@ func (db *MySQLDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpti
`excerpt`,
`author`,
`public`,
`modified`,
`created_at`,
`modified_at`,
`content <> "" has_content`}
if opts.WithContent {
@ -370,7 +378,7 @@ func (db *MySQLDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpti
case ByLastAdded:
query += ` ORDER BY id DESC`
case ByLastModified:
query += ` ORDER BY modified DESC`
query += ` ORDER BY modified_at DESC`
default:
query += ` ORDER BY id`
}
@ -563,7 +571,7 @@ func (db *MySQLDatabase) GetBookmark(ctx context.Context, id int, url string) (m
args := []interface{}{id}
query := `SELECT
id, url, title, excerpt, author, public,
content, html, modified, content <> '' has_content
content, html, modified_at, created_at, content <> '' has_content
FROM bookmark WHERE id = ?`
if url != "" {

View file

@ -57,6 +57,7 @@ var postgresMigrations = []migration{
return nil
}),
newFileMigration("0.3.0", "0.4.0", "postgres/0002_created_time"),
}
// PGDatabase is implementation of Database interface
@ -128,8 +129,8 @@ func (db *PGDatabase) SaveBookmarks(ctx context.Context, create bool, bookmarks
if err := db.withTx(ctx, func(tx *sqlx.Tx) error {
// Prepare statement
stmtInsertBook, err := tx.Preparex(`INSERT INTO bookmark
(url, title, excerpt, author, public, content, html, modified)
VALUES($1, $2, $3, $4, $5, $6, $7, $8)
(url, title, excerpt, author, public, content, html, modified_at, created_at)
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id`)
if err != nil {
return errors.WithStack(err)
@ -143,7 +144,7 @@ func (db *PGDatabase) SaveBookmarks(ctx context.Context, create bool, bookmarks
public = $5,
content = $6,
html = $7,
modified = $8
modified_at = $8
WHERE id = $9`)
if err != nil {
return errors.WithStack(err)
@ -187,20 +188,21 @@ func (db *PGDatabase) SaveBookmarks(ctx context.Context, create bool, bookmarks
}
// Set modified time
if book.Modified == "" {
book.Modified = modifiedTime
if book.ModifiedAt == "" {
book.ModifiedAt = modifiedTime
}
// Save bookmark
var err error
if create {
book.CreatedAt = modifiedTime
err = stmtInsertBook.QueryRowContext(ctx,
book.URL, book.Title, book.Excerpt, book.Author,
book.Public, book.Content, book.HTML, book.Modified).Scan(&book.ID)
book.Public, book.Content, book.HTML, book.ModifiedAt, book.CreatedAt).Scan(&book.ID)
} else {
_, err = stmtUpdateBook.ExecContext(ctx,
book.URL, book.Title, book.Excerpt, book.Author,
book.Public, book.Content, book.HTML, book.Modified, book.ID)
book.Public, book.Content, book.HTML, book.ModifiedAt, book.ID)
}
if err != nil {
return errors.WithStack(err)
@ -270,7 +272,8 @@ func (db *PGDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOptions
`excerpt`,
`author`,
`public`,
`modified`,
`created_at`,
`modified_at`,
`content <> '' has_content`}
if opts.WithContent {
@ -359,7 +362,7 @@ func (db *PGDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOptions
case ByLastAdded:
query += ` ORDER BY id DESC`
case ByLastModified:
query += ` ORDER BY modified DESC`
query += ` ORDER BY modified_at DESC`
default:
query += ` ORDER BY id`
}
@ -570,7 +573,7 @@ func (db *PGDatabase) GetBookmark(ctx context.Context, id int, url string) (mode
args := []interface{}{id}
query := `SELECT
id, url, title, excerpt, author, public,
content, html, modified, content <> '' has_content
content, html, modified_at, created_at, content <> '' has_content
FROM bookmark WHERE id = $1`
if url != "" {

View file

@ -59,6 +59,7 @@ var sqliteMigrations = []migration{
}),
newFileMigration("0.3.0", "0.4.0", "sqlite/0002_denormalize_content"),
newFileMigration("0.4.0", "0.5.0", "sqlite/0003_uniq_id"),
newFileMigration("0.5.0", "0.6.0", "sqlite/0004_created_time"),
}
// SQLiteDatabase is implementation of Database interface
@ -126,15 +127,15 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma
// Prepare statement
stmtInsertBook, err := tx.PreparexContext(ctx, `INSERT INTO bookmark
(url, title, excerpt, author, public, modified, has_content)
VALUES(?, ?, ?, ?, ?, ?, ?) RETURNING id`)
(url, title, excerpt, author, public, modified_at, has_content, created_at)
VALUES(?, ?, ?, ?, ?, ?, ?, ?) RETURNING id`)
if err != nil {
return errors.WithStack(err)
}
stmtUpdateBook, err := tx.PreparexContext(ctx, `UPDATE bookmark SET
url = ?, title = ?, excerpt = ?, author = ?,
public = ?, modified = ?, has_content = ?
public = ?, modified_at = ?, has_content = ?
WHERE id = ?`)
if err != nil {
return errors.WithStack(err)
@ -192,8 +193,8 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma
}
// Set modified time
if book.Modified == "" {
book.Modified = modifiedTime
if book.ModifiedAt == "" {
book.ModifiedAt = modifiedTime
}
hasContent := book.Content != ""
@ -201,11 +202,12 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma
// Create or update bookmark
var err error
if create {
book.CreatedAt = modifiedTime
err = stmtInsertBook.QueryRowContext(ctx,
book.URL, book.Title, book.Excerpt, book.Author, book.Public, book.Modified, hasContent).Scan(&book.ID)
book.URL, book.Title, book.Excerpt, book.Author, book.Public, book.ModifiedAt, hasContent, book.CreatedAt).Scan(&book.ID)
} else {
_, err = stmtUpdateBook.ExecContext(ctx,
book.URL, book.Title, book.Excerpt, book.Author, book.Public, book.Modified, hasContent, book.ID)
book.URL, book.Title, book.Excerpt, book.Author, book.Public, book.ModifiedAt, hasContent, book.ID)
}
if err != nil {
return errors.WithStack(err)
@ -297,7 +299,8 @@ func (db *SQLiteDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpt
b.excerpt,
b.author,
b.public,
b.modified,
b.created_at,
b.modified_at,
b.has_content
FROM bookmark b
WHERE 1`
@ -388,7 +391,7 @@ func (db *SQLiteDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpt
case ByLastAdded:
query += ` ORDER BY b.id DESC`
case ByLastModified:
query += ` ORDER BY b.modified DESC`
query += ` ORDER BY b.modified_at DESC`
default:
query += ` ORDER BY b.id`
}
@ -668,8 +671,8 @@ func (db *SQLiteDatabase) DeleteBookmarks(ctx context.Context, ids ...int) error
func (db *SQLiteDatabase) GetBookmark(ctx context.Context, id int, url string) (model.BookmarkDTO, bool, error) {
args := []interface{}{id}
query := `SELECT
b.id, b.url, b.title, b.excerpt, b.author, b.public, b.modified,
bc.content, bc.html, b.has_content
b.id, b.url, b.title, b.excerpt, b.author, b.public, b.modified_at,
bc.content, bc.html, b.has_content, b.created_at
FROM bookmark b
LEFT JOIN bookmark_content bc ON bc.docid = b.id
WHERE b.id = ?`

View file

@ -1,5 +1,5 @@
//go:build linux || windows || darwin
// +build linux windows darwin
//go:build linux || windows || darwin || freebsd
// +build linux windows darwin freebsd
package database

View file

@ -14,7 +14,8 @@ type BookmarkDTO struct {
Excerpt string `db:"excerpt" json:"excerpt"`
Author string `db:"author" json:"author"`
Public int `db:"public" json:"public"`
Modified string `db:"modified" json:"modified"`
CreatedAt string `db:"created_at" json:"createdAt"`
ModifiedAt string `db:"modified_at" json:"modifiedAt"`
Content string `db:"content" json:"-"`
HTML string `db:"html" json:"html,omitempty"`
ImageURL string `db:"image_url" json:"imageURL"`

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -410,7 +410,7 @@ export default {
},
{
name: "makePublic",
label: "Make archive publicly available",
label: "Make bookmark publicly available",
type: "check",
value: this.appOptions.MakePublic,
},
@ -517,7 +517,7 @@ export default {
},
{
name: "makePublic",
label: "Make archive publicly available",
label: "Make bookmark publicly available",
type: "check",
value: book.public >= 1,
},

View file

@ -45,7 +45,7 @@ var template = `
</label>
<label>
<input type="checkbox" v-model="appOptions.MakePublic" @change="saveSetting">
Make archive publicly available by default
Make bookmark publicly available by default
</label>
</details>
<details v-if="activeAccount.owner" open class="setting-group" id="setting-accounts">

View file

@ -165,7 +165,8 @@
}
}
@media (max-width: 600px) {
// Display bookmark menu items on small/medium screens
@media (max-width: 1024px) {
a {
display: block;
}

View file

@ -5,10 +5,57 @@ select {
color: var(--color);
}
footer {
.login-footer {
color: var(--color);
}
.content-footer {
width: 100%;
padding: 20px;
max-width: 840px;
margin-bottom: 16px;
background-color: var(--contentBg);
border: 1px solid var(--border);
display: flex;
flex-flow: column;
align-items: center;
}
.metadata {
display: flex;
flex-flow: row wrap;
text-align: center;
font-size: 16px;
color: var(--colorLink);
&:nth-child(1) {
justify-content: flex-start;
}
&:nth-child(2) {
justify-content: flex-end;
}
&[v-cloak] {
visibility: hidden;
}
}
.links {
display: flex;
flex-flow: row wrap;
a {
padding: 0 4px;
color: var(--color);
text-decoration: underline;
&:hover,
&:focus {
color: var(--main);
}
}
}
* {
border-width: 0;
box-sizing: border-box;
@ -384,18 +431,6 @@ a {
flex-flow: column;
align-items: center;
#metadata {
display: flex;
flex-flow: row wrap;
text-align: center;
font-size: 16px;
color: var(--colorLink);
&[v-cloak] {
visibility: hidden;
}
}
#title {
padding: 8px 0;
grid-column-start: 1;
@ -406,22 +441,6 @@ a {
hyphens: none;
text-align: center;
}
#links {
display: flex;
flex-flow: row wrap;
a {
padding: 0 4px;
color: var(--color);
text-decoration: underline;
&:hover,
&:focus {
color: var(--main);
}
}
}
}
#content {
@ -491,7 +510,6 @@ a {
#page-home {
> .empty-message {
width: 100%;
max-width: 400px;
font-size: 1em;
background-color: var(--contentBg);

View file

@ -16,16 +16,14 @@
<link href="assets/css/style.css" rel="stylesheet">
<script src="assets/js/dayjs.min.js"></script>
<script src="assets/js/vue.min.js"></script>
</head>
<body>
<div id="content-scene">
<div id="header">
<p id="metadata" v-cloak>Added {{localtime()}}</p>
<p id="title" dir="auto">$$.Book.Title$$</p>
<div id="links">
<div class="links">
<a href="$$.Book.URL$$" target="_blank" rel="noopener noreferrer">View Original</a>
$$if .Book.HasArchive$$
<a href="bookmark/$$.Book.ID$$/archive">View Archive</a>
@ -38,6 +36,9 @@
<div id="content" dir="auto" v-pre>
$$.HTML$$
</div>
<footer class="content-footer">
<p class="metadata">{{ createdModifiedTime() }} </p>
</footer>
</div>
<script type="module">
@ -48,15 +49,21 @@
el: '#content-scene',
mixins: [basePage],
data: {
modified: "$$.Book.Modified$$"
created: "$$.Book.CreatedAt$$"
},
methods: {
localtime() {
var strTime = this.modified.replace(" ", "T");
if (!strTime.endsWith("Z")) {
strTime += "Z";
}
return dayjs(strTime).format("D MMMM YYYY, HH:mm:ss");
createdModifiedTime() {
const strCreatedTime = "$$.Book.CreatedAt$$".replace(" ", "T") + ("$$.Book.CreatedAt$$".endsWith("Z") ? "" : "Z");
const strModifiedTime = "$$.Book.ModifiedAt$$".replace(" ", "T") + ("$$.Book.ModifiedAt$$".endsWith("Z") ? "" : "Z");
const createdDate = new Date(strCreatedTime);
const modifiedDate = new Date(strModifiedTime);
if (createdDate.toDateString() === modifiedDate.toDateString()) {
return `Added ${createdDate.getDate()} ${createdDate.toLocaleString('default', { month: 'long' })} ${createdDate.getFullYear()}`;
} else {
return `Added ${createdDate.getDate()} ${createdDate.toLocaleString('default', { month: 'long' })} ${createdDate.getFullYear()} | Last Modified ${modifiedDate.getDate()} ${modifiedDate.toLocaleString('default', {month: 'long'})} ${modifiedDate.getFullYear()}`;
}
},
loadSetting() {
var opts = JSON.parse(localStorage.getItem("shiori-account")) || {},

View file

@ -48,7 +48,7 @@
</div>
</form>
</div>
<footer>
<footer class="login-footer">
<p>$$.Version$$</p>
</footer>
</div>

View file

@ -359,6 +359,9 @@ func (h *Handler) ApiUpdateBookmark(w http.ResponseWriter, r *http.Request, ps h
}
}
// Set bookmark modified
book.ModifiedAt = ""
// Update database
res, err := h.DB.SaveBookmarks(ctx, false, book)
checkError(err)

View file

@ -12,8 +12,7 @@ case `uname -o` in
GNU/Linux)
# Detect support of avx2 in linux hosts
if ! grep -q avx2 /proc/cpuinfo; then
BUN="sde -chip-check-disable -- bun"
echo "Your CPU does not support avx2 so we use sde, for more information please look at https://github.com/oven-sh/bun/issues/762#issuecomment-1186505847"
echo "It seems that your CPU does not support AVX2, if you experience long build times (>1m) ensure that you use bun's baseline builds. More information at https://github.com/oven-sh/bun/issues/67"
fi
;;
esac