mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-23 16:40:36 +00:00
Merge branch 'main' into import-limit
This commit is contained in:
commit
b22d060d93
31 changed files with 189 additions and 33 deletions
|
@ -61,7 +61,7 @@ SEARCH_TIMEOUT=5
|
|||
QUERY_TIMEOUT=5
|
||||
|
||||
# Thumbnails Generation
|
||||
ENABLE_THUMBNAIL_GENERATION=false
|
||||
ENABLE_THUMBNAIL_GENERATION=true
|
||||
|
||||
# S3 configuration
|
||||
USE_S3=false
|
||||
|
|
6
.github/workflows/black.yml
vendored
6
.github/workflows/black.yml
vendored
|
@ -10,6 +10,6 @@ jobs:
|
|||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: psf/black@21.4b2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: psf/black@22.12.0
|
||||
|
|
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
|
@ -36,11 +36,11 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
@ -51,7 +51,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
@ -65,4 +65,4 @@ jobs:
|
|||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
|
2
.github/workflows/curlylint.yaml
vendored
2
.github/workflows/curlylint.yaml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install curlylint
|
||||
run: pip install curlylint
|
||||
|
|
4
.github/workflows/django-tests.yml
vendored
4
.github/workflows/django-tests.yml
vendored
|
@ -23,9 +23,9 @@ jobs:
|
|||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install Dependencies
|
||||
|
|
2
.github/workflows/lint-frontend.yaml
vendored
2
.github/workflows/lint-frontend.yaml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it.
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install modules
|
||||
run: npm install stylelint stylelint-config-recommended stylelint-config-standard stylelint-order eslint
|
||||
|
|
2
.github/workflows/prettier.yaml
vendored
2
.github/workflows/prettier.yaml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it.
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install modules
|
||||
run: npm install prettier
|
||||
|
|
4
.github/workflows/pylint.yml
vendored
4
.github/workflows/pylint.yml
vendored
|
@ -12,9 +12,9 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install Dependencies
|
||||
|
|
|
@ -92,3 +92,4 @@ class Author(BookData):
|
|||
bio: str = ""
|
||||
wikipediaLink: str = ""
|
||||
type: str = "Author"
|
||||
website: str = ""
|
||||
|
|
|
@ -15,6 +15,7 @@ class AuthorForm(CustomForm):
|
|||
"aliases",
|
||||
"bio",
|
||||
"wikipedia_link",
|
||||
"website",
|
||||
"born",
|
||||
"died",
|
||||
"openlibrary_key",
|
||||
|
@ -31,6 +32,7 @@ class AuthorForm(CustomForm):
|
|||
"wikipedia_link": forms.TextInput(
|
||||
attrs={"aria-describedby": "desc_wikipedia_link"}
|
||||
),
|
||||
"website": forms.TextInput(attrs={"aria-describedby": "desc_website"}),
|
||||
"born": forms.SelectDateWidget(attrs={"aria-describedby": "desc_born"}),
|
||||
"died": forms.SelectDateWidget(attrs={"aria-describedby": "desc_died"}),
|
||||
"oepnlibrary_key": forms.TextInput(
|
||||
|
|
33
bookwyrm/migrations/0173_auto_20221228_1436.py
Normal file
33
bookwyrm/migrations/0173_auto_20221228_1436.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-28 14:36
|
||||
|
||||
import bookwyrm.models.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0172_alter_user_preferred_language"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="author",
|
||||
name="website",
|
||||
field=bookwyrm.models.fields.CharField(
|
||||
blank=True, max_length=255, null=True
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="author",
|
||||
name="isfdb",
|
||||
field=bookwyrm.models.fields.CharField(blank=True, max_length=6, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="author",
|
||||
name="isni",
|
||||
field=bookwyrm.models.fields.CharField(
|
||||
blank=True, max_length=19, null=True
|
||||
),
|
||||
),
|
||||
]
|
|
@ -17,12 +17,15 @@ class Author(BookDataModel):
|
|||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
isni = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
max_length=19, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
gutenberg_id = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
isfdb = fields.CharField(
|
||||
max_length=6, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
website = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
# idk probably other keys would be useful here?
|
||||
|
|
|
@ -58,7 +58,7 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
|
|||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
isfdb = fields.CharField(
|
||||
max_length=255, blank=True, null=True, deduplication_field=True
|
||||
max_length=6, blank=True, null=True, deduplication_field=True
|
||||
)
|
||||
search_vector = SearchVectorField(null=True)
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<meta itemprop="name" content="{{ author.name }}">
|
||||
|
||||
{% firstof author.aliases author.born author.died as details %}
|
||||
{% firstof author.wikipedia_link author.openlibrary_key author.inventaire_id author.isni author.isfdb as links %}
|
||||
{% firstof author.wikipedia_link author.website author.openlibrary_key author.inventaire_id author.isni author.isfdb as links %}
|
||||
{% if details or links %}
|
||||
<div class="column is-3">
|
||||
{% if details %}
|
||||
|
@ -73,6 +73,14 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if author.website %}
|
||||
<div>
|
||||
<a itemprop="sameAs" href="{{ author.website }}" rel="nofollow noopener noreferrer" target="_blank">
|
||||
{% trans "Website" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if author.isni %}
|
||||
<div class="mt-1">
|
||||
<a itemprop="sameAs" href="{{ author.isni_link }}" rel="nofollow noopener noreferrer" target="_blank">
|
||||
|
|
|
@ -57,6 +57,10 @@
|
|||
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.wikipedia_link.errors id="desc_wikipedia_link" %}
|
||||
|
||||
<p class="field"><label class="label" for="id_website">{% trans "Website:" %}</label> {{ form.website }}</p>
|
||||
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.website.errors id="desc_website" %}
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="id_born">{% trans "Birth date:" %}</label>
|
||||
<input type="date" name="born" value="{{ form.born.value|date:'Y-m-d' }}" class="input" id="id_born">
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
type="radio"
|
||||
name="rating"
|
||||
value="{{ forloop.counter0 }}.5"
|
||||
{% if default_rating > 0 and default_rating >= forloop.counter0 %}checked{% endif %}
|
||||
{% if default_rating > 0 and default_rating > forloop.counter0 %}checked{% endif %}
|
||||
/>
|
||||
<input
|
||||
id="{{ type|slugify }}_book{{ book.id }}_star_{{ forloop.counter }}"
|
||||
|
|
|
@ -13,7 +13,7 @@ Finish "<em>{{ book_title }}</em>"
|
|||
{% csrf_token %}
|
||||
<input type="hidden" name="id" value="{{ readthrough.id }}">
|
||||
<input type="hidden" name="reading_status" value="read">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block reading-dates %}
|
||||
|
|
|
@ -10,9 +10,9 @@ Start "<em>{{ book_title }}</em>"
|
|||
|
||||
{% block modal-form-open %}
|
||||
<form name="start-reading-{{ uuid }}" action="{% url 'reading-status' 'start' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="reading_status" value="reading">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
{% csrf_token %}
|
||||
{% endblock %}
|
||||
|
||||
{% block reading-dates %}
|
||||
|
|
|
@ -10,9 +10,9 @@ Want to Read "<em>{{ book_title }}</em>"
|
|||
|
||||
{% block modal-form-open %}
|
||||
<form name="want-to-read-{{ uuid }}" action="{% url 'reading-status' 'want' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="reading_status" value="to-read">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
{% csrf_token %}
|
||||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<input type="hidden" name="change-shelf-from" value="{{ current.identifier }}">
|
||||
<input type="hidden" name="shelf" value="{{ shelf.identifier }}">
|
||||
|
||||
<button class="button is-fullwidth is-small shelf-option is-radiusless has-background-body" type="submit" {% if shelf.identifier == current.identifier %}disabled{% endif %}>
|
||||
<span>
|
||||
{% include "snippets/translated_shelf_name.html" with shelf=shelf %}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
from io import BytesIO
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
from dateutil.parser import parse
|
||||
from PIL import Image
|
||||
from django.core.files.base import ContentFile
|
||||
|
@ -10,6 +12,7 @@ from django.utils import timezone
|
|||
|
||||
from bookwyrm import models, settings
|
||||
from bookwyrm.models.book import isbn_10_to_13, isbn_13_to_10
|
||||
from bookwyrm.settings import ENABLE_THUMBNAIL_GENERATION
|
||||
|
||||
|
||||
class Book(TestCase):
|
||||
|
@ -101,6 +104,10 @@ class Book(TestCase):
|
|||
self.first_edition.save()
|
||||
self.assertEqual(self.first_edition.edition_rank, 1)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not ENABLE_THUMBNAIL_GENERATION,
|
||||
reason="Thumbnail generation disabled in settings",
|
||||
)
|
||||
def test_thumbnail_fields(self):
|
||||
"""Just hit them"""
|
||||
image_file = pathlib.Path(__file__).parent.joinpath(
|
||||
|
|
|
@ -3,6 +3,7 @@ import re
|
|||
from django.test import TestCase
|
||||
|
||||
from bookwyrm.utils import regex
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
|
||||
|
||||
class TestUtils(TestCase):
|
||||
|
@ -11,3 +12,20 @@ class TestUtils(TestCase):
|
|||
def test_regex(self):
|
||||
"""Regexes used throughout the app"""
|
||||
self.assertTrue(re.match(regex.DOMAIN, "xn--69aa8bzb.xn--y9a3aq"))
|
||||
|
||||
def test_valid_url_domain(self):
|
||||
"""Check with a valid URL"""
|
||||
self.assertEqual(
|
||||
validate_url_domain("https://your.domain.here/legit-book-url/"),
|
||||
"https://your.domain.here/legit-book-url/",
|
||||
)
|
||||
|
||||
def test_invalid_url_domain(self):
|
||||
"""Check with an invalid URL"""
|
||||
self.assertEqual(
|
||||
validate_url_domain("https://up-to-no-good.tld/bad-actor.exe"), "/"
|
||||
)
|
||||
|
||||
def test_default_url_domain(self):
|
||||
"""Check with a default URL"""
|
||||
self.assertEqual(validate_url_domain("/"), "/")
|
||||
|
|
19
bookwyrm/utils/validate.py
Normal file
19
bookwyrm/utils/validate.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
"""Validations"""
|
||||
from bookwyrm.settings import DOMAIN, USE_HTTPS
|
||||
|
||||
|
||||
def validate_url_domain(url, default="/"):
|
||||
"""Basic check that the URL starts with the instance domain name"""
|
||||
if not url:
|
||||
return default
|
||||
|
||||
if url in ("/", default):
|
||||
return url
|
||||
|
||||
protocol = "https://" if USE_HTTPS else "http://"
|
||||
origin = f"{protocol}{DOMAIN}"
|
||||
|
||||
if url.startswith(origin):
|
||||
return url
|
||||
|
||||
return default
|
|
@ -12,6 +12,7 @@ from django.views.decorators.http import require_POST
|
|||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.views.shelf.shelf_actions import unshelve
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
from .status import CreateStatus
|
||||
from .helpers import get_edition, handle_reading_status, is_api_request
|
||||
from .helpers import load_date_in_user_tz_as_utc
|
||||
|
@ -42,6 +43,8 @@ class ReadingStatus(View):
|
|||
@transaction.atomic
|
||||
def post(self, request, status, book_id):
|
||||
"""Change the state of a book by shelving it and adding reading dates"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
identifier = {
|
||||
"want": models.Shelf.TO_READ,
|
||||
"start": models.Shelf.READING,
|
||||
|
@ -83,7 +86,7 @@ class ReadingStatus(View):
|
|||
if current_status_shelfbook.shelf.identifier != desired_shelf.identifier:
|
||||
current_status_shelfbook.delete()
|
||||
else: # It already was on the shelf
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
models.ShelfBook.objects.create(
|
||||
book=book, shelf=desired_shelf, user=request.user
|
||||
|
@ -121,7 +124,7 @@ class ReadingStatus(View):
|
|||
if is_api_request(request):
|
||||
return HttpResponse()
|
||||
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.db import IntegrityError, transaction
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.views.decorators.http import require_POST
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
|
||||
from bookwyrm import forms, models
|
||||
|
||||
|
@ -35,6 +36,8 @@ def delete_shelf(request, shelf_id):
|
|||
@transaction.atomic
|
||||
def shelve(request):
|
||||
"""put a book on a user's shelf"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
book = get_object_or_404(models.Edition, id=request.POST.get("book"))
|
||||
desired_shelf = get_object_or_404(
|
||||
request.user.shelf_set, identifier=request.POST.get("shelf")
|
||||
|
@ -64,13 +67,14 @@ def shelve(request):
|
|||
.first()
|
||||
)
|
||||
if current_read_status_shelfbook is not None:
|
||||
# If it is not already on the shelf
|
||||
if (
|
||||
current_read_status_shelfbook.shelf.identifier
|
||||
!= desired_shelf.identifier
|
||||
):
|
||||
current_read_status_shelfbook.delete()
|
||||
else: # It is already on the shelf
|
||||
return redirect("/")
|
||||
else:
|
||||
return redirect(next_step)
|
||||
|
||||
# create the new shelf-book entry
|
||||
models.ShelfBook.objects.create(
|
||||
|
@ -86,13 +90,16 @@ def shelve(request):
|
|||
# Might be good to alert, or reject the action?
|
||||
except IntegrityError:
|
||||
pass
|
||||
return redirect("/")
|
||||
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def unshelve(request, book_id=False):
|
||||
"""remove a book from a user's shelf"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
identity = book_id if book_id else request.POST.get("book")
|
||||
book = get_object_or_404(models.Edition, id=identity)
|
||||
shelf_book = get_object_or_404(
|
||||
|
@ -100,4 +107,4 @@ def unshelve(request, book_id=False):
|
|||
)
|
||||
shelf_book.raise_not_deletable(request.user)
|
||||
shelf_book.delete()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
|
|
@ -18,6 +18,7 @@ from django.views.decorators.http import require_POST
|
|||
from markdown import markdown
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.utils import regex, sanitizer
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
from .helpers import handle_remote_webfinger, is_api_request
|
||||
from .helpers import load_date_in_user_tz_as_utc
|
||||
|
||||
|
@ -58,6 +59,8 @@ class CreateStatus(View):
|
|||
# pylint: disable=too-many-branches
|
||||
def post(self, request, status_type, existing_status_id=None):
|
||||
"""create status of whatever type"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
created = not existing_status_id
|
||||
existing_status = None
|
||||
if existing_status_id:
|
||||
|
@ -80,7 +83,7 @@ class CreateStatus(View):
|
|||
if is_api_request(request):
|
||||
logger.exception(form.errors)
|
||||
return HttpResponseBadRequest()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
status = form.save(request, commit=False)
|
||||
status.ready = False
|
||||
|
@ -134,7 +137,7 @@ class CreateStatus(View):
|
|||
|
||||
if is_api_request(request):
|
||||
return HttpResponse()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
@ -167,6 +170,8 @@ def update_progress(request, book_id): # pylint: disable=unused-argument
|
|||
def edit_readthrough(request):
|
||||
"""can't use the form because the dates are too finnicky"""
|
||||
# TODO: remove this, it duplicates the code in the ReadThrough view
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
readthrough = get_object_or_404(models.ReadThrough, id=request.POST.get("id"))
|
||||
|
||||
readthrough.start_date = load_date_in_user_tz_as_utc(
|
||||
|
@ -198,7 +203,7 @@ def edit_readthrough(request):
|
|||
|
||||
if is_api_request(request):
|
||||
return HttpResponse()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
def find_mentions(user, content):
|
||||
|
|
5
contrib/README.md
Normal file
5
contrib/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Contrib
|
||||
|
||||
This directory contain some scripts, configuration files and other useful tools around BookWyrm.
|
||||
|
||||
These tools are not necessary for the proper functioning of BookWyrm but provide a helpful leg-up for integration with some third-party or to nicely fit BookWyrm into other environments.
|
14
contrib/systemd/bookwyrm-scheduler.service
Normal file
14
contrib/systemd/bookwyrm-scheduler.service
Normal file
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=BookWyrm scheduler
|
||||
After=network.target postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
User=bookwyrm
|
||||
Group=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
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
14
contrib/systemd/bookwyrm-worker.service
Normal file
14
contrib/systemd/bookwyrm-worker.service
Normal file
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=BookWyrm worker
|
||||
After=network.target postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
User=bookwyrm
|
||||
Group=bookwyrm
|
||||
WorkingDirectory=/opt/bookwyrm/
|
||||
ExecStart=/opt/bookwyrm/venv/bin/celery -A celerywyrm worker -l info -Q high_priority,medium_priority,low_priority
|
||||
StandardOutput=journal
|
||||
StandardError=inherit
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
14
contrib/systemd/bookwyrm.service
Normal file
14
contrib/systemd/bookwyrm.service
Normal file
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=BookWyrm
|
||||
After=network.target postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
User=bookwyrm
|
||||
Group=bookwyrm
|
||||
WorkingDirectory=/opt/bookwyrm/
|
||||
ExecStart=/opt/bookwyrm/venv/bin/gunicorn bookwyrm.wsgi:application --bind 0.0.0.0:8000
|
||||
StandardOutput=journal
|
||||
StandardError=inherit
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -15,7 +15,7 @@ services:
|
|||
- static_volume:/app/static
|
||||
- media_volume:/app/images
|
||||
db:
|
||||
image: postgres
|
||||
image: postgres:13
|
||||
env_file: .env
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
|
Loading…
Reference in a new issue