1
0
mirror of https://github.com/aquatix/digimarks.git synced 2025-12-06 22:05:09 +01:00

Only fetch new bookmarks and tags when cache is not up-to-date

This commit is contained in:
2025-05-06 14:44:25 +02:00
parent 127284716e
commit 3642753266
3 changed files with 86 additions and 16 deletions

View File

@@ -19,7 +19,7 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from pydantic import AnyUrl, DirectoryPath, FilePath from pydantic import AnyUrl, DirectoryPath, FilePath
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from sqlmodel import AutoString, Field, Session, SQLModel, create_engine, select from sqlmodel import AutoString, Field, Session, SQLModel, create_engine, desc, select
DIGIMARKS_USER_AGENT = 'digimarks/2.0.0-dev' DIGIMARKS_USER_AGENT = 'digimarks/2.0.0-dev'
DIGIMARKS_VERSION = '2.0.0a1' DIGIMARKS_VERSION = '2.0.0a1'
@@ -381,7 +381,11 @@ def get_bookmark(
url_hash: str, url_hash: str,
) -> Bookmark: ) -> Bookmark:
"""Show bookmark details.""" """Show bookmark details."""
bookmark = session.exec(select(Bookmark).where(Bookmark.userkey == user_key, Bookmark.url_hash == url_hash)).first() bookmark = session.exec(
select(Bookmark).where(
Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED
)
).first()
# bookmark = session.get(Bookmark, {'url_hash': url_hash, 'userkey': user_key}) # bookmark = session.get(Bookmark, {'url_hash': url_hash, 'userkey': user_key})
return bookmark return bookmark
@@ -450,13 +454,42 @@ def delete_bookmark(
return {'ok': True} return {'ok': True}
@app.get('/api/v1/{user_key}/latest_changes/')
def bookmarks_changed_since(
session: SessionDep,
user_key: str,
):
"""Last update on server, so the (browser) client knows whether to fetch an update."""
latest_modified_bookmark = session.exec(
select(Bookmark)
.where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
.order_by(desc(Bookmark.modified_date))
).first()
latest_created_bookmark = session.exec(
select(Bookmark)
.where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
.order_by(desc(Bookmark.created_date))
).first()
latest_modification = max(latest_modified_bookmark.modified_date, latest_created_bookmark.created_date)
return {
'current_time': datetime.now(UTC),
'latest_change': latest_modified_bookmark.modified_date,
'latest_created': latest_created_bookmark.created_date,
'latest_modification': latest_modification,
}
@app.get('/api/v1/{user_key}/tags/') @app.get('/api/v1/{user_key}/tags/')
def list_tags_for_user( def list_tags_for_user(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
) -> list[str]: ) -> list[str]:
"""List all tags in use by the user.""" """List all tags in use by the user."""
bookmarks = session.exec(select(Bookmark).where(Bookmark.userkey == user_key)).all() bookmarks = session.exec(
select(Bookmark).where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
).all()
tags = [] tags = []
for bookmark in bookmarks: for bookmark in bookmarks:
tags += bookmark.tags_list tags += bookmark.tags_list

View File

@@ -9,12 +9,18 @@ document.addEventListener('alpine:init', () => {
bookmarks: [], bookmarks: [],
tags: [], tags: [],
tryout: 'hey', /* Loading indicator */
loading: false, loading: false,
/* Search filter */
search: '', search: '',
/* Sort on ~ */
sort_title_asc: Alpine.$persist(false).as('sort_title_asc'),
sort_title_desc: Alpine.$persist(false).as('sort_title_desc'),
sort_created_asc: Alpine.$persist(false).as('sort_created_asc'),
sort_created_desc: Alpine.$persist(false).as('sort_created_desc'),
async init() { async init() {
/** Initialise the application after loading */ /** Initialise the application after loading */
// if (this.userKey in this.cache) { // if (this.userKey in this.cache) {
@@ -28,28 +34,47 @@ document.addEventListener('alpine:init', () => {
}, 1000); }, 1000);
}, },
async loadCache() { async loadCache() {
console.log('Loading bookmarks from cache for user "' + this.userKey + '"'); if (this.userKey in this.cache) {
this.bookmarks = this.cache[this.userKey]['bookmarks'] || []; console.log('Loading bookmarks from cache for user "' + this.userKey + '"');
this.bookmarks = this.cache[this.userKey]['bookmarks'] || [];
}
}, },
async getBookmarks() { async getBookmarks() {
/** Get the bookmarks from the backend */ /** Get the bookmarks from the backend */
this.loading = true; this.loading = true;
if (!(this.userKey in this.cache)) {
/* There is no cache for this userKey yet, create on */
console.log('Creating cache for user "' + this.userKey + '"');
this.cache[this.userKey] = {'bookmarks': [], 'latest_changes': {}};
}
let latest_status_response = await fetch('/api/v1/' + this.userKey + '/latest_changes/');
let latest_status_result = await latest_status_response.json();
let should_fetch = false;
let latest_modification_in_cache = this.cache[this.userKey].latest_changes.latest_modification || "0000-00-00";
should_fetch = latest_status_result.latest_modification > latest_modification_in_cache;
this.cache[this.userKey].latest_changes = latest_status_result;
if (!should_fetch) {
console.log('Cache is up-to-date');
this.loading = false;
return;
}
console.log('Fetching latest bookmarks from backend for user "' + this.userKey + '"...'); console.log('Fetching latest bookmarks from backend for user "' + this.userKey + '"...');
let response = await fetch('/api/v1/' + this.userKey + '/bookmarks/?limit=10000'); let response = await fetch('/api/v1/' + this.userKey + '/bookmarks/?limit=10000');
let result = await response.json(); let result = await response.json();
console.log(result);
this.bookmarks = result; this.bookmarks = result;
if (!(this.userKey in this.cache)) {
/* There is no cache for this userKey yet, create on */
console.log('caching');
this.cache[this.userKey] = {'bookmarks': []};
}
/* Cache the bookmarks to Local Storage */ /* Cache the bookmarks to Local Storage */
this.cache[this.userKey]['bookmarks'] = result; this.cache[this.userKey]['bookmarks'] = result;
let tags_response = await fetch('/api/v1/' + this.userKey + '/tags/');
let tags_result = await tags_response.json();
this.cache[this.userKey]['tags'] = tags_result;
this.loading = false; this.loading = false;
}, },
get filteredItems() { get filteredBookmarks() {
// return this.cache[this.userKey]['bookmarks'].filter( // return this.cache[this.userKey]['bookmarks'].filter(
// i => i.title.includes(this.search) // i => i.title.includes(this.search)
// ) // )
@@ -58,6 +83,11 @@ document.addEventListener('alpine:init', () => {
i => i.title.match(new RegExp(this.search, "i")) i => i.title.match(new RegExp(this.search, "i"))
) )
}, },
get filteredTags() {
return this.cache[this.userKey].tags.filter(
i => i.match(new RegExp(this.search, "i"))
)
},
async sortAlphabetically(order = 'asc') { async sortAlphabetically(order = 'asc') {
if (order === 'desc') { if (order === 'desc') {
this.bookmarks.sort((a, b) => b.title.localeCompare(a.title)); this.bookmarks.sort((a, b) => b.title.localeCompare(a.title));

View File

@@ -31,10 +31,17 @@
</p> </p>
<ul x-cloak> <ul x-cloak>
<template x-for="bookmark in $store.digimarks.filteredItems" :key="bookmark.id"> <template x-for="bookmark in $store.digimarks.filteredBookmarks" :key="bookmark.id">
<li><a x-text="bookmark.title" x-bind:href="bookmark.url" target="_blank"></a></li> <li><a x-text="bookmark.title" x-bind:href="bookmark.url" target="_blank"></a></li>
</template> </template>
</ul> </ul>
<h2>tags</h2>
<ul x-cloak>
<template x-for="tag in $store.digimarks.filteredTags" :key="tag">
<li x-text="tag"></li>
</template>
</ul>
</main> </main>
</div> </div>
{% endblock %} {% endblock %}