diff --git a/src/digimarks/bookmarks_service.py b/src/digimarks/bookmarks_service.py index 21a6533..0d02875 100644 --- a/src/digimarks/bookmarks_service.py +++ b/src/digimarks/bookmarks_service.py @@ -10,7 +10,8 @@ import bs4 import httpx from extract_favicon import from_html from fastapi import Query, Request -from pydantic import AnyUrl +from fastapi.exceptions import HTTPException +from pydantic import AnyUrl, ValidationError from sqlmodel import select from digimarks import tags_service, utils @@ -34,7 +35,9 @@ async def set_information_from_source(bookmark: Bookmark, request: Request) -> B """Request the title by requesting the source url.""" logger.info('Extracting information from url %s', bookmark.url) try: - result = await request.app.requests_client.get(bookmark.url, headers={'User-Agent': DIGIMARKS_USER_AGENT}) + result = await request.app.state.requests_client.get( + str(bookmark.url), headers={'User-Agent': DIGIMARKS_USER_AGENT} + ) bookmark.http_status = result.status_code except httpx.HTTPError as err: # For example, "MissingSchema: Invalid URL 'abc': No schema supplied. Perhaps you meant http://abc?" @@ -43,11 +46,12 @@ async def set_information_from_source(bookmark: Bookmark, request: Request) -> B bookmark.title = '' return bookmark if bookmark.http_status == 200 or bookmark.http_status == 202: - html = bs4.BeautifulSoup(result.text, 'html.parser') + html_content = bs4.BeautifulSoup(result.text, 'html.parser') try: - bookmark.title = html.title.text.strip() - except AttributeError: - bookmark.title = '' + bookmark.title = html_content.title.text.strip() + except AttributeError as exc: + logger.error('Error while trying to extract title from URL %s: %s', str(bookmark.url), str(exc)) + raise HTTPException(status_code=400, detail='Error while trying to extract title') url_parts = urlparse(str(bookmark.url)) root_url = url_parts.scheme + '://' + url_parts.netloc @@ -72,11 +76,15 @@ def strip_url_params(url: str) -> str: return urlunparse((parsed.scheme, parsed.netloc, parsed.path, parsed.params, '', parsed.fragment)) -def update_bookmark_with_info(bookmark: Bookmark, request: Request, strip_params: bool = False): +async def update_bookmark_with_info(bookmark: Bookmark, request: Request, strip_params: bool = False): """Automatically update title, favicon, etc.""" + if isinstance(bookmark.url, str): + # If type of the url is a 'simple' string, ensure it to be an AnyUrl + bookmark.url = AnyUrl(bookmark.url) + if not bookmark.title: # Title was empty, automatically fetch it from the url, will also update the status code - set_information_from_source(bookmark, request) + await set_information_from_source(bookmark, request) if strip_params: # Strip URL parameters, e.g., tracking params @@ -129,7 +137,12 @@ async def autocomplete_bookmark( bookmark.user_key = user_key # Auto-fill title, fix tags etc. - update_bookmark_with_info(bookmark, request, strip_params) + try: + await update_bookmark_with_info(bookmark, request, strip_params) + except ValidationError as exc: + logger.error('ValidationError while autocompleting bookmark with URL %s', bookmark.url) + logger.error('Error was: %s', str(exc)) + raise HTTPException(status_code=400, detail='Error while autocompleting, likely the URL contained an error') url_hash = utils.generate_hash(str(bookmark.url)) result = await session.exec( @@ -157,7 +170,7 @@ async def add_bookmark( bookmark.user_key = user_key # Auto-fill title, fix tags etc. - update_bookmark_with_info(bookmark, request, strip_params) + await update_bookmark_with_info(bookmark, request, strip_params) bookmark.url_hash = utils.generate_hash(str(bookmark.url)) logger.info('Adding bookmark %s for user %s', bookmark.url_hash, user_key) @@ -193,7 +206,7 @@ async def update_bookmark( bookmark_db.sqlmodel_update(bookmark_data) # Autofill title, fix tags, etc. where (still) needed - update_bookmark_with_info(bookmark, request, strip_params) + await update_bookmark_with_info(bookmark, request, strip_params) session.add(bookmark_db) await session.commit() diff --git a/src/digimarks/static/js/digimarks.js b/src/digimarks/static/js/digimarks.js index 1c7fe36..17866ee 100644 --- a/src/digimarks/static/js/digimarks.js +++ b/src/digimarks/static/js/digimarks.js @@ -214,10 +214,12 @@ document.addEventListener('alpine:init', () => { resetEditBookmark() { this.bookmarkToEdit = { + 'url_hash': '', 'url': '', 'title': '', 'note': '', - 'tags': '' + 'tags': '', + 'strip_params': false } }, async startAddingBookmark() { @@ -233,7 +235,55 @@ document.addEventListener('alpine:init', () => { console.log('Bookmark URL changed'); // let response = await fetch('/api/v1/' + this.userKey + '/autocomplete_bookmark/'); try { - const response = await fetch('/api/v1/' + this.userKey + '/autocomplete_bookmark/', { + const response = await fetch('/api/v1/' + this.userKey + '/autocomplete_bookmark/?strip_params=' + this.bookmarkToEdit.strip_params, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + // Bookmark form data + url_hash: this.bookmarkToEdit.url_hash, + url: this.bookmarkToEdit.url, + title: this.bookmarkToEdit.title, + note: this.bookmarkToEdit.note, + tags: this.bookmarkToEdit.tags + }) + }); + const data = await response.json(); + // TODO: update form fields if needed (auto-fetched title for example + console.log('Got response'); + console.log(response); + console.log(data); + if (data.ok) { + this.bookmarkToEdit.url_hash = data.url_hash; + this.bookmarkToEdit.url = data.url; + this.bookmarkToEdit.title = data.title; + this.bookmarkToEdit.note = data.note; + this.bookmarkToEdit.tags = data.tags; + } else { + console.log('Error occurred'); + this.bookmarkToEditError = data.detail; + } + // this.bookmarkToEditError = 'lolwut'; + } catch (error) { + // enter your logic for when there is an error (ex. error toast) + + console.log('error occurred'); + console.log(error); + this.bookmarkToEditError = error.detail; + console.log('yesssh?'); + } + }, + async saveBookmark() { + console.log('Saving bookmark'); + // this.bookmarkToEditVisible = false; + // this.show_bookmark_details = false; + }, + async addBookmark() { + /* Post new bookmark to the backend */ + console.log('Adding bookmark'); + try { + const response = await fetch('/api/v1/' + this.userKey + '/add_bookmark/?strip_params=' + this.bookmarkToEdit.strip_params, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -249,21 +299,12 @@ document.addEventListener('alpine:init', () => { const data = await response.json(); // TODO: update form fields if needed (auto-fetched title for example console.log(data); - this.bookmarkToEditError = 'lolwut'; + // this.bookmarkToEditError = 'lolwut'; } catch (error) { // enter your logic for when there is an error (ex. error toast) console.log(error) } - }, - async saveBookmark() { - console.log('Saving bookmark'); - // this.bookmarkToEditVisible = false; - // this.show_bookmark_details = false; - }, - async addBookmark() { - /* Post new bookmark to the backend */ - // } }) }); diff --git a/src/digimarks/templates/user_index.html b/src/digimarks/templates/user_index.html index 530bc09..d8ce269 100644 --- a/src/digimarks/templates/user_index.html +++ b/src/digimarks/templates/user_index.html @@ -212,16 +212,17 @@ x-model="$store.digimarks.bookmarkToEdit.tags">
+ x-text="$store.digimarks.bookmarkToEditError" x-cloak class="error">