diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..ed29060e6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +'trailingComma': 'es5' \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..faef31a43 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.7.0 diff --git a/bookwyrm/activitypub/__init__.py b/bookwyrm/activitypub/__init__.py index 05ca44476..2697620f0 100644 --- a/bookwyrm/activitypub/__init__.py +++ b/bookwyrm/activitypub/__init__.py @@ -4,7 +4,11 @@ import sys from .base_activity import ActivityEncoder, Signature, naive_parse from .base_activity import Link, Mention, Hashtag -from .base_activity import ActivitySerializerError, resolve_remote_id +from .base_activity import ( + ActivitySerializerError, + resolve_remote_id, + get_representative, +) from .image import Document, Image from .note import Note, GeneratedNote, Article, Comment, Quotation from .note import Review, Rating diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index d0c3c7fd3..9e05c03af 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -201,14 +201,13 @@ class Book(BookDataModel): @property def alt_text(self): """image alt test""" - text = self.title - if self.edition_info: - text += f" ({self.edition_info})" - return text + author = f"{name}: " if (name := self.author_text) else "" + edition = f" ({info})" if (info := self.edition_info) else "" + return f"{author}{self.title}{edition}" def save(self, *args: Any, **kwargs: Any) -> None: """can't be abstract for query reasons, but you shouldn't USE it""" - if not isinstance(self, Edition) and not isinstance(self, Work): + if not isinstance(self, (Edition, Work)): raise ValueError("Books should be added as Editions or Works") return super().save(*args, **kwargs) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index d21c9363d..28effaf9b 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -1,5 +1,6 @@ """ activitypub-aware django model fields """ from dataclasses import MISSING +from datetime import datetime import re from uuid import uuid4 from urllib.parse import urljoin @@ -534,8 +535,10 @@ class DateTimeField(ActivitypubFieldMixin, models.DateTimeField): return value.isoformat() def field_from_activity(self, value, allow_external_connections=True): + missing_fields = datetime(1970, 1, 1) # "2022-10" => "2022-10-01" try: - date_value = dateutil.parser.parse(value) + # TODO(dato): investigate `ignoretz=True` wrt bookwyrm#3028. + date_value = dateutil.parser.parse(value, default=missing_fields) try: return timezone.make_aware(date_value) except ValueError: diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 8929e9037..f5d86ad2e 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -1,4 +1,5 @@ """ track progress of goodreads imports """ +from datetime import datetime import math import re import dateutil.parser @@ -259,38 +260,30 @@ class ImportItem(models.Model): except ValueError: return None + def _parse_datefield(self, field, /): + if not (date := self.normalized_data.get(field)): + return None + + defaults = datetime(1970, 1, 1) # "2022-10" => "2022-10-01" + parsed = dateutil.parser.parse(date, default=defaults) + + # Keep timezone if import already had one, else use default. + return parsed if timezone.is_aware(parsed) else timezone.make_aware(parsed) + @property def date_added(self): """when the book was added to this dataset""" - if self.normalized_data.get("date_added"): - parsed_date_added = dateutil.parser.parse( - self.normalized_data.get("date_added") - ) - - if timezone.is_aware(parsed_date_added): - # Keep timezone if import already had one - return parsed_date_added - - return timezone.make_aware(parsed_date_added) - return None + return self._parse_datefield("date_added") @property def date_started(self): """when the book was started""" - if self.normalized_data.get("date_started"): - return timezone.make_aware( - dateutil.parser.parse(self.normalized_data.get("date_started")) - ) - return None + return self._parse_datefield("date_started") @property def date_read(self): """the date a book was completed""" - if self.normalized_data.get("date_finished"): - return timezone.make_aware( - dateutil.parser.parse(self.normalized_data.get("date_finished")) - ) - return None + return self._parse_datefield("date_finished") @property def reads(self): diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 5d6109468..11646431b 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -366,7 +366,8 @@ class Quotation(BookStatus): quote = re.sub(r"^
", '
"', self.quote) quote = re.sub(r"
$", '"', quote) title, href = self.book.title, self.book.remote_id - citation = f'— {title}' + author = f"{name}: " if (name := self.book.author_text) else "" + citation = f'— {author}{title}' if position := self._format_position(): citation += f", {position}" return f"{quote}{citation}
{self.content}" diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 9a4c9b5a4..16241f9df 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -4,6 +4,7 @@ from typing import AnyStr from environs import Env + import requests from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ImproperlyConfigured @@ -14,7 +15,13 @@ from django.core.exceptions import ImproperlyConfigured env = Env() env.read_env() DOMAIN = env("DOMAIN") -VERSION = "0.6.5" + +with open("VERSION", encoding="utf-8") as f: + version = f.read() + version = version.replace("\n", "") +f.close() + +VERSION = version RELEASE_API = env( "RELEASE_API", @@ -24,7 +31,7 @@ RELEASE_API = env( PAGE_LENGTH = env.int("PAGE_LENGTH", 15) DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English") -JS_CACHE = "b972a43c" +JS_CACHE = "ac315a3b" # email EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend") @@ -317,6 +324,7 @@ LANGUAGES = [ LANGUAGE_ARTICLES = { "English": {"the", "a", "an"}, + "Español (Spanish)": {"un", "una", "unos", "unas", "el", "la", "los", "las"}, } TIME_ZONE = "UTC" diff --git a/bookwyrm/static/css/bookwyrm.scss b/bookwyrm/static/css/bookwyrm.scss index 437795457..17d6d9119 100644 --- a/bookwyrm/static/css/bookwyrm.scss +++ b/bookwyrm/static/css/bookwyrm.scss @@ -1,4 +1,3 @@ @charset "utf-8"; - -@import "vendor/bulma/bulma.sass"; -@import "bookwyrm/all.scss"; +@import "vendor/bulma/bulma"; +@import "bookwyrm/all"; diff --git a/bookwyrm/static/css/bookwyrm/_all.scss b/bookwyrm/static/css/bookwyrm/_all.scss index 1e8569827..7e50fe3e1 100644 --- a/bookwyrm/static/css/bookwyrm/_all.scss +++ b/bookwyrm/static/css/bookwyrm/_all.scss @@ -16,9 +16,7 @@ @import "components/status"; @import "components/tabs"; @import "components/toggle"; - @import "overrides/bulma_overrides"; - @import "utilities/a11y"; @import "utilities/alignments"; @import "utilities/colors"; @@ -40,10 +38,12 @@ body { width: 12px; height: 12px; } + ::-webkit-scrollbar-thumb { background: $scrollbar-thumb; border-radius: 0.5em; } + ::-webkit-scrollbar-track { background: $scrollbar-track; } @@ -89,7 +89,6 @@ button::-moz-focus-inner { /** Utilities not covered by Bulma ******************************************************************************/ - .tag.is-small { height: auto; } @@ -144,7 +143,6 @@ button.button-paragraph { vertical-align: middle; } - /** States ******************************************************************************/ @@ -159,7 +157,6 @@ button.button-paragraph { cursor: not-allowed; } - /* Notifications page ******************************************************************************/ diff --git a/bookwyrm/static/css/bookwyrm/components/_book_cover.scss b/bookwyrm/static/css/bookwyrm/components/_book_cover.scss index 48b564a0b..db9391cc1 100644 --- a/bookwyrm/static/css/bookwyrm/components/_book_cover.scss +++ b/bookwyrm/static/css/bookwyrm/components/_book_cover.scss @@ -43,7 +43,7 @@ max-height: 100%; /* Useful when stretching under-sized images. */ - image-rendering: optimizeQuality; + image-rendering: optimizequality; image-rendering: smooth; } diff --git a/bookwyrm/static/css/bookwyrm/components/_copy.scss b/bookwyrm/static/css/bookwyrm/components/_copy.scss index 7a47c1dba..2595401cc 100644 --- a/bookwyrm/static/css/bookwyrm/components/_copy.scss +++ b/bookwyrm/static/css/bookwyrm/components/_copy.scss @@ -30,20 +30,20 @@ } .copy-tooltip { - overflow: visible; - visibility: hidden; - width: 140px; - background-color: #555; - color: #fff; - text-align: center; - border-radius: 6px; - padding: 5px; - position: absolute; - z-index: 1; - margin-left: -30px; - margin-top: -45px; - opacity: 0; - transition: opacity 0.3s; + overflow: visible; + visibility: hidden; + width: 140px; + background-color: #555; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px; + position: absolute; + z-index: 1; + margin-left: -30px; + margin-top: -45px; + opacity: 0; + transition: opacity 0.3s; } .copy-tooltip::after { @@ -54,5 +54,5 @@ margin-left: -60px; border-width: 5px; border-style: solid; - border-color: #555 transparent transparent transparent; - } + border-color: #555 transparent transparent; +} diff --git a/bookwyrm/static/css/bookwyrm/components/_tabs.scss b/bookwyrm/static/css/bookwyrm/components/_tabs.scss index 2d68a383b..8e00f6a88 100644 --- a/bookwyrm/static/css/bookwyrm/components/_tabs.scss +++ b/bookwyrm/static/css/bookwyrm/components/_tabs.scss @@ -44,12 +44,12 @@ .bw-tabs a:hover { border-bottom-color: transparent; - color: $text + color: $text; } .bw-tabs a.is-active { border-bottom-color: transparent; - color: $link + color: $link; } .bw-tabs.is-left { diff --git a/bookwyrm/static/css/fonts/icomoon.eot b/bookwyrm/static/css/fonts/icomoon.eot index b86375a90..33dc07eec 100644 Binary files a/bookwyrm/static/css/fonts/icomoon.eot and b/bookwyrm/static/css/fonts/icomoon.eot differ diff --git a/bookwyrm/static/css/fonts/icomoon.svg b/bookwyrm/static/css/fonts/icomoon.svg index a3c902a07..058c19226 100644 --- a/bookwyrm/static/css/fonts/icomoon.svg +++ b/bookwyrm/static/css/fonts/icomoon.svg @@ -43,6 +43,8 @@- {% comment %} @todo The publisher property needs to be an Organization or a Person. We’ll be using Thing which is the more generic ancestor. @see https://schema.org/Publisher @@ -60,14 +57,14 @@ {% endfor %} {% endif %} - {% if date and publisher %} + {% with date=book.published_date|default:book.first_published_date|naturalday publisher=book.publishers|join:', ' %} + {% if book.published_date and publisher %} {% blocktrans %}Published {{ date }} by {{ publisher }}.{% endblocktrans %} - {% elif date %} - {% blocktrans %}Published {{ date }}{% endblocktrans %} {% elif publisher %} {% blocktrans %}Published by {{ publisher }}.{% endblocktrans %} + {% elif date %} + {% blocktrans %}Published {{ date }}{% endblocktrans %} {% endif %} + {% endwith %}
-{% endif %} -{% endwith %} {% endspaceless %} diff --git a/bookwyrm/templates/book/rating.html b/bookwyrm/templates/book/rating.html index c0af67fab..9998a49dc 100644 --- a/bookwyrm/templates/book/rating.html +++ b/bookwyrm/templates/book/rating.html @@ -5,13 +5,18 @@ {% include 'snippets/avatar.html' with user=user %} -{% trans "rated it" %}
- {% include 'snippets/stars.html' with rating=rating.rating %}- {% blocktrans count days=import_limit_reset with display_size=import_size_limit|intcomma %} + {% blocktrans trimmed count days=import_limit_reset with display_size=import_size_limit|intcomma %} Currently, you are allowed to import {{ display_size }} books every {{ import_limit_reset }} day. {% plural %} Currently, you are allowed to import {{ import_size_limit }} books every {{ import_limit_reset }} days. diff --git a/bookwyrm/templates/layout.html b/bookwyrm/templates/layout.html index e6f8a6f84..b8459856c 100644 --- a/bookwyrm/templates/layout.html +++ b/bookwyrm/templates/layout.html @@ -14,6 +14,7 @@ + {% block opengraph %} {% include 'snippets/opengraph.html' %} @@ -129,7 +130,12 @@
— Author Name: ' + "Test Edition
" + ), + ) + self.assertEqual( + activity["attachment"][0]["name"], "Author Name: Test Edition (worm)" + ) + def test_quotation_page_serialization(self, *_): """serialization of quotation page position""" tests = [ diff --git a/bookwyrm/tests/views/imports/test_import.py b/bookwyrm/tests/views/imports/test_import.py index ea88b197d..7dd87d4c2 100644 --- a/bookwyrm/tests/views/imports/test_import.py +++ b/bookwyrm/tests/views/imports/test_import.py @@ -7,6 +7,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory +from django.utils import timezone from bookwyrm import forms, models, views from bookwyrm.tests.validate_html import validate_html @@ -128,7 +129,7 @@ class ImportViews(TestCase): def test_get_average_import_time_with_data(self): """Now, with data""" - now = datetime.datetime.now() + now = timezone.now() two_hours_ago = now - datetime.timedelta(hours=2) four_hours_ago = now - datetime.timedelta(hours=4) models.ImportJob.objects.create( @@ -152,7 +153,7 @@ class ImportViews(TestCase): def test_get_average_import_time_ignore_stopped(self): """Don't include stopped, do include no status""" - now = datetime.datetime.now() + now = timezone.now() two_hours_ago = now - datetime.timedelta(hours=2) four_hours_ago = now - datetime.timedelta(hours=4) models.ImportJob.objects.create( diff --git a/bookwyrm/tests/views/test_search.py b/bookwyrm/tests/views/test_search.py index bf7bb2a5b..28f8268e3 100644 --- a/bookwyrm/tests/views/test_search.py +++ b/bookwyrm/tests/views/test_search.py @@ -156,7 +156,7 @@ class Views(TestCase): response = view(request) validate_html(response.render()) - self.assertFalse("results" in response.context_data) + self.assertTrue("results" in response.context_data) def test_search_lists(self): """searches remote connectors""" diff --git a/bookwyrm/tests/views/test_setup.py b/bookwyrm/tests/views/test_setup.py index 5f15604fe..7b8da3c33 100644 --- a/bookwyrm/tests/views/test_setup.py +++ b/bookwyrm/tests/views/test_setup.py @@ -72,7 +72,7 @@ class SetupViews(TestCase): self.site.refresh_from_db() self.assertFalse(self.site.install_mode) - user = models.User.objects.get() + user = models.User.objects.first() self.assertTrue(user.is_active) self.assertTrue(user.is_superuser) self.assertTrue(user.is_staff) diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 4c6bdf151..e5c144e58 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -34,6 +34,12 @@ urlpatterns = [ "robots.txt", TemplateView.as_view(template_name="robots.txt", content_type="text/plain"), ), + path( + "manifest.json", + TemplateView.as_view( + template_name="manifest.json", content_type="application/json" + ), + ), # federation endpoints re_path(r"^inbox/?$", views.Inbox.as_view(), name="inbox"), re_path(rf"{LOCAL_USER_PATH}/inbox/?$", views.Inbox.as_view(), name="user_inbox"), diff --git a/bookwyrm/views/search.py b/bookwyrm/views/search.py index bc3b2aa57..2b7303fd7 100644 --- a/bookwyrm/views/search.py +++ b/bookwyrm/views/search.py @@ -91,18 +91,15 @@ def book_search(request): def user_search(request): - """cool kids members only user search""" + """user search: search for a user""" viewer = request.user query = request.GET.get("q") query = query.strip() data = {"type": "user", "query": query} - # logged out viewers can't search users - if not viewer.is_authenticated: - return TemplateResponse(request, "search/user.html", data) # use webfinger for mastodon style account@domain.com username to load the user if # they don't exist locally (handle_remote_webfinger will check the db) - if re.match(regex.FULL_USERNAME, query): + if re.match(regex.FULL_USERNAME, query) and viewer.is_authenticated: handle_remote_webfinger(query) results = ( @@ -118,6 +115,11 @@ def user_search(request): ) .order_by("-similarity") ) + + # don't expose remote users + if not viewer.is_authenticated: + results = results.filter(local=True) + paginated = Paginator(results, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) data["results"] = page diff --git a/bookwyrm/views/setup.py b/bookwyrm/views/setup.py index 188c6a0ae..37344300d 100644 --- a/bookwyrm/views/setup.py +++ b/bookwyrm/views/setup.py @@ -9,6 +9,7 @@ from django.shortcuts import redirect from django.template.response import TemplateResponse from django.views import View +from bookwyrm.activitypub import get_representative from bookwyrm import forms, models from bookwyrm import settings from bookwyrm.utils import regex @@ -96,4 +97,5 @@ class CreateAdmin(View): login(request, user) site.install_mode = False site.save() + get_representative() # create the instance user return redirect("settings-site") diff --git a/bump-version.sh b/bump-version.sh new file mode 100755 index 000000000..552d8409d --- /dev/null +++ b/bump-version.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +NOW="$(date +'%B %d, %Y')" +RED="\033[1;31m" +GREEN="\033[0;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +WHITE="\033[1;37m" +RESET="\033[0m" + +LATEST_HASH=`git log --pretty=format:'%h' -n 1` + +QUESTION_FLAG="${GREEN}?" +WARNING_FLAG="${YELLOW}!" +NOTICE_FLAG="${CYAN}❯" + +# ADJUSTMENTS_MSG="${QUESTION_FLAG} ${CYAN}Now you can make adjustments to ${WHITE}CHANGELOG.md${CYAN}. Then press enter to continue." +PUSHING_MSG="${NOTICE_FLAG} Pushing new version to the ${WHITE}origin${CYAN}..." + +if [ -f VERSION ]; then + BASE_STRING=`cat VERSION` + BASE_LIST=(`echo $BASE_STRING | tr '.' ' '`) + V_MAJOR=${BASE_LIST[0]} + V_MINOR=${BASE_LIST[1]} + V_PATCH=${BASE_LIST[2]} + echo -e "${NOTICE_FLAG} Current version: ${WHITE}$BASE_STRING" + echo -e "${NOTICE_FLAG} Latest commit hash: ${WHITE}$LATEST_HASH" + V_MINOR=$((V_MINOR + 1)) + V_PATCH=0 + SUGGESTED_VERSION="$V_MAJOR.$V_MINOR.$V_PATCH" + echo -ne "${QUESTION_FLAG} ${CYAN}Enter a version number [${WHITE}$SUGGESTED_VERSION${CYAN}]: " + read INPUT_STRING + if [ "$INPUT_STRING" = "" ]; then + INPUT_STRING=$SUGGESTED_VERSION + fi + echo -e "${NOTICE_FLAG} Will set new version to be ${WHITE}$INPUT_STRING" + echo $INPUT_STRING > VERSION +# echo "## $INPUT_STRING ($NOW)" > tmpfile +# git log --pretty=format:" - %s" "v$BASE_STRING"...HEAD >> tmpfile +# echo "" >> tmpfile +# echo "" >> tmpfile +# cat CHANGELOG.md >> tmpfile +# mv tmpfile CHANGELOG.md +# echo -e "$ADJUSTMENTS_MSG" +# read + echo -e "$PUSHING_MSG" +# git add CHANGELOG.md + git add VERSION + git commit -m "Bump version to ${INPUT_STRING}." + git tag -a -m "Tag version ${INPUT_STRING}." "v$INPUT_STRING" + git push origin --tags +else + echo -e "${WARNING_FLAG} Could not find a VERSION file." + echo -ne "${QUESTION_FLAG} ${CYAN}Do you want to create a version file and start from scratch? [${WHITE}y${CYAN}]: " + read RESPONSE + if [ "$RESPONSE" = "" ]; then RESPONSE="y"; fi + if [ "$RESPONSE" = "Y" ]; then RESPONSE="y"; fi + if [ "$RESPONSE" = "Yes" ]; then RESPONSE="y"; fi + if [ "$RESPONSE" = "yes" ]; then RESPONSE="y"; fi + if [ "$RESPONSE" = "YES" ]; then RESPONSE="y"; fi + if [ "$RESPONSE" = "y" ]; then + echo "0.1.0" > VERSION +# echo "## 0.1.0 ($NOW)" > CHANGELOG.md +# git log --pretty=format:" - %s" >> CHANGELOG.md +# echo "" >> CHANGELOG.md +# echo "" >> CHANGELOG.md +# echo -e "$ADJUSTMENTS_MSG" +# read + echo -e "$PUSHING_MSG" +# git add CHANGELOG.md + git add VERSION + git commit -m "Add VERSION and CHANGELOG.md files, Bump version to v0.1.0." + git tag -a -m "Tag version 0.1.0." "v0.1.0" + git push origin --tags + fi +fi + +echo -e "${NOTICE_FLAG} Finished." diff --git a/bw-dev b/bw-dev index c81fa023b..57441f013 100755 --- a/bw-dev +++ b/bw-dev @@ -188,27 +188,25 @@ case "$CMD" in ;; prettier) prod_error - $DOCKER_COMPOSE run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js + $DOCKER_COMPOSE run --rm dev-tools prettier --write bookwyrm/static/js/*.js ;; eslint) prod_error - $DOCKER_COMPOSE run --rm dev-tools npx eslint bookwyrm/static --ext .js + $DOCKER_COMPOSE run --rm dev-tools eslint bookwyrm/static --ext .js ;; stylelint) prod_error - $DOCKER_COMPOSE run --rm dev-tools npx stylelint \ - bookwyrm/static/css/bookwyrm.scss bookwyrm/static/css/bookwyrm/**/*.scss --fix \ - --config dev-tools/.stylelintrc.js + $DOCKER_COMPOSE run --rm dev-tools stylelint --fix bookwyrm/static/css \ + --config dev-tools/.stylelintrc.js --ignore-path dev-tools/.stylelintignore ;; formatters) prod_error runweb pylint bookwyrm/ $DOCKER_COMPOSE run --rm dev-tools black celerywyrm bookwyrm - $DOCKER_COMPOSE run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js - $DOCKER_COMPOSE run --rm dev-tools npx eslint bookwyrm/static --ext .js - $DOCKER_COMPOSE run --rm dev-tools npx stylelint \ - bookwyrm/static/css/bookwyrm.scss bookwyrm/static/css/bookwyrm/**/*.scss --fix \ - --config dev-tools/.stylelintrc.js + $DOCKER_COMPOSE run --rm dev-tools prettier --write bookwyrm/static/js/*.js + $DOCKER_COMPOSE run --rm dev-tools eslint bookwyrm/static --ext .js + $DOCKER_COMPOSE run --rm dev-tools stylelint --fix bookwyrm/static/css \ + --config dev-tools/.stylelintrc.js --ignore-path dev-tools/.stylelintignore ;; mypy) prod_error diff --git a/contrib/systemd/bookwyrm-scheduler.service b/contrib/systemd/bookwyrm-scheduler.service index f3572f632..1d5b05214 100644 --- a/contrib/systemd/bookwyrm-scheduler.service +++ b/contrib/systemd/bookwyrm-scheduler.service @@ -5,10 +5,30 @@ After=network.target postgresql.service redis.service [Service] User=bookwyrm Group=bookwyrm -WorkingDirectory=/opt/bookwyrm/ +WorkingDirectory=/opt/bookwyrm ExecStart=/opt/bookwyrm/venv/bin/celery -A celerywyrm beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler StandardOutput=journal StandardError=inherit +ProtectSystem=strict +ProtectHome=tmpfs +InaccessiblePaths=-/media -/mnt -/srv +PrivateTmp=yes +TemporaryFileSystem=/var /run /opt +PrivateUsers=true +PrivateDevices=true +BindReadOnlyPaths=/opt/bookwyrm +BindPaths=/opt/bookwyrm/images /opt/bookwyrm/static /var/run/postgresql +LockPersonality=yes +MemoryDenyWriteExecute=true +PrivateMounts=true +ProtectHostname=true +ProtectClock=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +RestrictRealtime=true +RestrictNamespaces=net [Install] WantedBy=multi-user.target diff --git a/contrib/systemd/bookwyrm-worker.service b/contrib/systemd/bookwyrm-worker.service index ebba8b6ca..b9406a66e 100644 --- a/contrib/systemd/bookwyrm-worker.service +++ b/contrib/systemd/bookwyrm-worker.service @@ -5,10 +5,30 @@ After=network.target postgresql.service redis.service [Service] User=bookwyrm Group=bookwyrm -WorkingDirectory=/opt/bookwyrm/ +WorkingDirectory=/opt/bookwyrm ExecStart=/opt/bookwyrm/venv/bin/celery -A celerywyrm worker -l info -Q high_priority,medium_priority,low_priority,streams,images,suggested_users,email,connectors,lists,inbox,imports,import_triggered,broadcast,misc StandardOutput=journal StandardError=inherit +ProtectSystem=strict +ProtectHome=tmpfs +InaccessiblePaths=-/media -/mnt -/srv +PrivateTmp=yes +TemporaryFileSystem=/var /run /opt +PrivateUsers=true +PrivateDevices=true +BindReadOnlyPaths=/opt/bookwyrm +BindPaths=/opt/bookwyrm/images /opt/bookwyrm/static /var/run/postgresql +LockPersonality=yes +MemoryDenyWriteExecute=true +PrivateMounts=true +ProtectHostname=true +ProtectClock=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +RestrictRealtime=true +RestrictNamespaces=net [Install] WantedBy=multi-user.target diff --git a/contrib/systemd/bookwyrm.service b/contrib/systemd/bookwyrm.service index c7ebe26ec..6e9434aa3 100644 --- a/contrib/systemd/bookwyrm.service +++ b/contrib/systemd/bookwyrm.service @@ -5,10 +5,30 @@ After=network.target postgresql.service redis.service [Service] User=bookwyrm Group=bookwyrm -WorkingDirectory=/opt/bookwyrm/ +WorkingDirectory=/opt/bookwyrm ExecStart=/opt/bookwyrm/venv/bin/gunicorn bookwyrm.wsgi:application --bind 0.0.0.0:8000 StandardOutput=journal StandardError=inherit +ProtectSystem=strict +ProtectHome=tmpfs +InaccessiblePaths=-/media -/mnt -/srv +PrivateTmp=yes +TemporaryFileSystem=/var /run /opt +PrivateUsers=true +PrivateDevices=true +BindReadOnlyPaths=/opt/bookwyrm +BindPaths=/opt/bookwyrm/images /opt/bookwyrm/static /var/run/postgresql +LockPersonality=yes +MemoryDenyWriteExecute=true +PrivateMounts=true +ProtectHostname=true +ProtectClock=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +RestrictRealtime=true +RestrictNamespaces=net [Install] WantedBy=multi-user.target diff --git a/dev-tools/.stylelintignore b/dev-tools/.stylelintignore index b2cd33f89..441f5eb72 100644 --- a/dev-tools/.stylelintignore +++ b/dev-tools/.stylelintignore @@ -1 +1,2 @@ **/vendor/** +**/fonts/** diff --git a/dev-tools/.stylelintrc.js b/dev-tools/.stylelintrc.js index 7ab51b9b3..a3403e89c 100644 --- a/dev-tools/.stylelintrc.js +++ b/dev-tools/.stylelintrc.js @@ -1,7 +1,7 @@ /* global module */ module.exports = { - "extends": "stylelint-config-standard", + "extends": "stylelint-config-standard-scss", "plugins": [ "stylelint-order" @@ -18,5 +18,13 @@ module.exports = { "declaration-block-no-redundant-longhand-properties": null, "no-descending-specificity": null, "alpha-value-notation": null - } + }, + "overrides": [ + { + "files": [ "../**/themes/bookwyrm-*.scss" ], + "rules": { + "no-invalid-position-at-import-rule": null + } + } + ] }; diff --git a/dev-tools/Dockerfile b/dev-tools/Dockerfile index 9abc7491e..f6a7bb793 100644 --- a/dev-tools/Dockerfile +++ b/dev-tools/Dockerfile @@ -1,14 +1,17 @@ FROM python:3.9 +WORKDIR /app/dev-tools -ENV PYTHONUNBUFFERED 1 +ENV PATH="/app/dev-tools/node_modules/.bin:$PATH" +ENV PYTHONUNBUFFERED=1 +ENV NPM_CONFIG_UPDATE_NOTIFIER=false +ENV PIP_ROOT_USER_ACTION=ignore PIP_DISABLE_PIP_VERSION_CHECK=1 + +COPY nodejs.sources /etc/apt/sources.list.d/ +COPY package.json requirements.txt .stylelintrc.js .stylelintignore /app/dev-tools/ + +RUN apt-get update && \ + apt-get install -y nodejs && \ + pip install -r requirements.txt && \ + npm install . -RUN mkdir /app WORKDIR /app - -COPY package.json requirements.txt .stylelintrc.js .stylelintignore /app/ -RUN pip install -r requirements.txt - -RUN apt-get update && apt-get install -y curl -RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - -RUN apt-get install -y nodejs && apt-get clean -RUN npm install . diff --git a/dev-tools/nodejs.sources b/dev-tools/nodejs.sources new file mode 100644 index 000000000..1e0c3ba40 --- /dev/null +++ b/dev-tools/nodejs.sources @@ -0,0 +1,34 @@ +Types: deb +URIs: https://deb.nodesource.com/node_18.x +Suites: nodistro +Components: main +Signed-By: + -----BEGIN PGP PUBLIC KEY BLOCK----- + . + mQENBFdDN1ABCADaNd/I3j3tn40deQNgz7hB2NvT+syXe6k4ZmdiEcOfBvFrkS8B + hNS67t93etHsxEy7E0qwsZH32bKazMqe9zDwoa3aVImryjh6SHC9lMtW27JPHFeM + Srkt9YmH1WMwWcRO6eSY9B3PpazquhnvbammLuUojXRIxkDroy6Fw4UKmUNSRr32 + 9Ej87jRoR1B2/57Kfp2Y4+vFGGzSvh3AFQpBHq51qsNHALU6+8PjLfIt+5TPvaWR + TB+kAZnQZkaIQM2nr1n3oj6ak2RATY/+kjLizgFWzgEfbCrbsyq68UoY5FPBnu4Z + E3iDZpaIqwKr0seUC7iA1xM5eHi5kty1oB7HABEBAAG0Ik5Tb2xpZCA8bnNvbGlk + LWdwZ0Bub2Rlc291cmNlLmNvbT6JATgEEwECACIFAldDN1ACGwMGCwkIBwMCBhUI + AgkKCwQWAgMBAh4BAheAAAoJEC9ZtfmbG+C0y7wH/i4xnab36dtrYW7RZwL8i6Sc + NjMx4j9+U1kr/F6YtqWd+JwCbBdar5zRghxPcYEq/qf7MbgAYcs1eSOuTOb7n7+o + xUwdH2iCtHhKh3Jr2mRw1ks7BbFZPB5KmkxHaEBfLT4d+I91ZuUdPXJ+0SXs9gzk + Dbz65Uhoz3W03aiF8HeL5JNARZFMbHHNVL05U1sTGTCOtu+1c/33f3TulQ/XZ3Y4 + hwGCpLe0Tv7g7Lp3iLMZMWYPEa0a7S4u8he5IEJQLd8bE8jltcQvrdr3Fm8kI2Jg + BJmUmX4PSfhuTCFaR/yeCt3UoW883bs9LfbTzIx9DJGpRIu8Y0IL3b4sj/GoZVq5 + AQ0EV0M3UAEIAKrTaC62ayzqOIPa7nS90BHHck4Z33a2tZF/uof38xNOiyWGhT8u + JeFoTTHn5SQq5Ftyu4K3K2fbbpuu/APQF05AaljzVkDGNMW4pSkgOasdysj831cu + ssrHX2RYS22wg80k6C/Hwmh5F45faEuNxsV+bPx7oPUrt5n6GMx84vEP3i1+FDBi + 0pt/B/QnDFBXki1BGvJ35f5NwDefK8VaInxXP3ZN/WIbtn5dqxppkV/YkO7GiJlp + Jlju9rf3kKUIQzKQWxFsbCAPIHoWv7rH9RSxgDithXtG6Yg5R1aeBbJaPNXL9wpJ + YBJbiMjkAFaz4B95FOqZm3r7oHugiCGsHX0AEQEAAYkBHwQYAQIACQUCV0M3UAIb + DAAKCRAvWbX5mxvgtE/OB/0VN88DR3Y3fuqy7lq/dthkn7Dqm9YXdorZl3L152eE + IF882aG8FE3qZdaLGjQO4oShAyNWmRfSGuoH0XERXAI9n0r8m4mDMxE6rtP7tHet + y/5M8x3CTyuMgx5GLDaEUvBusnTD+/v/fBMwRK/cZ9du5PSG4R50rtst+oYyC2ao + x4I2SgjtF/cY7bECsZDplzatN3gv34PkcdIg8SLHAVlL4N5tzumDeizRspcSyoy2 + K2+hwKU4C4+dekLLTg8rjnRROvplV2KtaEk6rxKtIRFDCoQng8wfJuIMrDNKvqZw + FRGt7cbvW5MCnuH8MhItOl9Uxp1wHp6gtav/h8Gp6MBa + =MARt + -----END PGP PUBLIC KEY BLOCK----- diff --git a/dev-tools/package.json b/dev-tools/package.json index 3fbc940cb..f7b996b77 100644 --- a/dev-tools/package.json +++ b/dev-tools/package.json @@ -11,6 +11,7 @@ "stylelint-config-recommended": "^7.0.0", "stylelint-config-standard": "^25.0.0", "stylelint-order": "^5.0.0", + "stylelint-config-standard-scss": "^3.0.0", "watch": "^0.13.0" }, "dependencies": { diff --git a/docker-compose.yml b/docker-compose.yml index c327c10a9..4d4037681 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,6 +101,7 @@ services: build: dev-tools env_file: .env volumes: + - /app/dev-tools/ - .:/app volumes: pgdata: diff --git a/locale/ca_ES/LC_MESSAGES/django.mo b/locale/ca_ES/LC_MESSAGES/django.mo index 81207d30b..f62f704e5 100644 Binary files a/locale/ca_ES/LC_MESSAGES/django.mo and b/locale/ca_ES/LC_MESSAGES/django.mo differ diff --git a/locale/ca_ES/LC_MESSAGES/django.po b/locale/ca_ES/LC_MESSAGES/django.po index 8f58c7269..6a58b0720 100644 --- a/locale/ca_ES/LC_MESSAGES/django.po +++ b/locale/ca_ES/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-30 17:48+0000\n" -"PO-Revision-Date: 2023-07-05 23:30\n" +"POT-Creation-Date: 2023-09-27 01:11+0000\n" +"PO-Revision-Date: 2023-09-28 06:50\n" "Last-Translator: Mouse Reeve