You've already forked hotpocket
BTHLABS-66: Prepping for public release: Take four
Apple gonna Apple... ;)
This commit is contained in:
@@ -158,6 +158,15 @@ method's name in the UI and defaults to `OIDC`.
|
|||||||
|
|
||||||
**NOTE:** Currently, only Keycloak has been tested with this login method.
|
**NOTE:** Currently, only Keycloak has been tested with this login method.
|
||||||
|
|
||||||
|
### Volumes
|
||||||
|
|
||||||
|
Both images declare `/srv/run` to be a volume. It's intended to keep the
|
||||||
|
service's runtime data, including but not limited to PID files, UNIX sockets
|
||||||
|
etc. It's recommended to persist this volume.
|
||||||
|
|
||||||
|
Additionally, the `deployment` image declares `/srv/uploads` to be a volume.
|
||||||
|
It's recommeded to persist this volume.
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
_HotPocket_ is developed by [BTHLabs](https://www.bthlabs.pl/).
|
_HotPocket_ is developed by [BTHLabs](https://www.bthlabs.pl/).
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ COPY --chown=$APP_USER_UID:$APP_USER_GID packages/common/ /srv/packages/common/
|
|||||||
COPY --chown=$APP_USER_UID:$APP_USER_GID packages/soa/ /srv/packages/soa/
|
COPY --chown=$APP_USER_UID:$APP_USER_GID packages/soa/ /srv/packages/soa/
|
||||||
|
|
||||||
RUN poetry install --only main,deployment && \
|
RUN poetry install --only main,deployment && \
|
||||||
minify -i --css-precision 0 --js-precision 0 --js-version 2022 hotpocket_backend/apps/ui/static/ui/css/hotpocket-backend*.css hotpocket_backend/apps/ui/static/ui/js/hotpocket*.js && \
|
minify -i --css-precision 0 --js-precision 0 --js-version 2022 hotpocket_backend/apps/ui/static/ui/css/hotpocket-backend*.css hotpocket_backend/apps/ui/static/ui/js/hotpocket-backend*.js && \
|
||||||
./manage.py collectstatic --settings hotpocket_backend.settings.deployment.build --noinput && \
|
./manage.py collectstatic --settings hotpocket_backend.settings.deployment.build --noinput && \
|
||||||
find hotpocket_backend/static/ -name "*.map*" -delete && \
|
find hotpocket_backend/static/ -name "*.map*" -delete && \
|
||||||
rm -f hotpocket_backend/settings/deployment/build.py && \
|
rm -f hotpocket_backend/settings/deployment/build.py && \
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from .access_token import AccessToken # noqa: F401
|
from .access_token import AccessToken # noqa: F401
|
||||||
from .account import AccountAdmin # noqa: F401
|
from .account import AccountAdmin # noqa: F401
|
||||||
|
from .auth_key import AuthKey # noqa: F401
|
||||||
|
|||||||
@@ -2,16 +2,41 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from hotpocket_backend.apps.accounts.models import AccessToken
|
from hotpocket_backend.apps.accounts.models import AccessToken
|
||||||
|
|
||||||
|
|
||||||
class AccessTokenAdmin(admin.ModelAdmin):
|
class AccessTokenAdmin(admin.ModelAdmin):
|
||||||
list_display = ('pk', 'account_uuid', 'origin', 'created_at', 'is_active')
|
list_display = (
|
||||||
|
'pk', 'account_uuid', 'origin', 'created_at', 'render_is_active',
|
||||||
|
)
|
||||||
search_fields = ('pk', 'account_uuid', 'key', 'origin')
|
search_fields = ('pk', 'account_uuid', 'key', 'origin')
|
||||||
|
readonly_fields = (
|
||||||
|
'pk',
|
||||||
|
'account_uuid',
|
||||||
|
'key',
|
||||||
|
'origin',
|
||||||
|
'meta',
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
)
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def has_change_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return request.user.is_superuser
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
@admin.display(
|
||||||
|
description=_('Is Active?'), boolean=True, ordering='-deleted_at',
|
||||||
|
)
|
||||||
|
def render_is_active(self, obj: AccessToken | None = None) -> bool | None:
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return obj.is_active
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(AccessToken, AccessTokenAdmin)
|
admin.site.register(AccessToken, AccessTokenAdmin)
|
||||||
|
|||||||
@@ -3,15 +3,26 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from hotpocket_backend.apps.accounts.models import Account
|
from hotpocket_backend.apps.accounts.models import Account
|
||||||
|
|
||||||
|
|
||||||
class AccountAdmin(UserAdmin):
|
class AccountAdmin(UserAdmin):
|
||||||
list_display = (*UserAdmin.list_display, 'is_active')
|
list_display = (*UserAdmin.list_display, 'render_is_active')
|
||||||
|
ordering = ['username']
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return request.user.is_superuser
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
@admin.display(
|
||||||
|
description=_('Is Active?'), boolean=True, ordering='-is_active',
|
||||||
|
)
|
||||||
|
def render_is_active(self, obj: Account | None = None) -> bool | None:
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return obj.is_active
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Account, AccountAdmin)
|
admin.site.register(Account, AccountAdmin)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.accounts.models import AuthKey
|
||||||
|
|
||||||
|
|
||||||
|
class AuthKeyAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
'pk', 'account_uuid', 'key', 'created_at', 'consumed_at', 'render_is_active',
|
||||||
|
)
|
||||||
|
search_fields = ('pk', 'account_uuid', 'key')
|
||||||
|
readonly_fields = (
|
||||||
|
'pk',
|
||||||
|
'account_uuid',
|
||||||
|
'key',
|
||||||
|
'consumed_at',
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
)
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def has_change_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
@admin.display(
|
||||||
|
description=_('Is Active?'), boolean=True, ordering='-deleted_at',
|
||||||
|
)
|
||||||
|
def render_is_active(self, obj: AuthKey | None = None) -> bool | None:
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return obj.is_active
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(AuthKey, AuthKeyAdmin)
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
from . import association # noqa: F401
|
||||||
from . import save # noqa: F401
|
from . import save # noqa: F401
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.saves.models import Association
|
||||||
|
|
||||||
|
|
||||||
|
class AssociationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
'pk', 'account_uuid', 'target', 'created_at', 'render_is_active',
|
||||||
|
)
|
||||||
|
search_fields = ('pk', 'account_uuid')
|
||||||
|
fields = (
|
||||||
|
'pk',
|
||||||
|
'account_uuid',
|
||||||
|
'archived_at',
|
||||||
|
'starred_at',
|
||||||
|
'target_meta',
|
||||||
|
'target_title',
|
||||||
|
'target_description',
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
)
|
||||||
|
readonly_fields = (
|
||||||
|
'pk',
|
||||||
|
'account_uuid',
|
||||||
|
'target_meta',
|
||||||
|
'target',
|
||||||
|
'archived_at',
|
||||||
|
'starred_at',
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
)
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
@admin.display(
|
||||||
|
description=_('Is Active?'), boolean=True, ordering='-deleted_at',
|
||||||
|
)
|
||||||
|
def render_is_active(self, obj: Association | None = None) -> bool | None:
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return obj.is_active
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Association, AssociationAdmin)
|
||||||
@@ -11,6 +11,26 @@ class SaveAdmin(admin.ModelAdmin):
|
|||||||
list_display = (
|
list_display = (
|
||||||
'pk', 'key', 'account_uuid', 'created_at', 'render_is_active',
|
'pk', 'key', 'account_uuid', 'created_at', 'render_is_active',
|
||||||
)
|
)
|
||||||
|
search_fields = ('pk', 'account_uuid', 'url')
|
||||||
|
fields = (
|
||||||
|
'pk',
|
||||||
|
'account_uuid',
|
||||||
|
'key',
|
||||||
|
'url',
|
||||||
|
'title',
|
||||||
|
'description',
|
||||||
|
'last_processed_at',
|
||||||
|
'is_netloc_banned',
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
)
|
||||||
|
readonly_fields = (
|
||||||
|
'pk',
|
||||||
|
'account_uuid',
|
||||||
|
'key',
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
)
|
||||||
ordering = ['-created_at']
|
ordering = ['-created_at']
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
onLoad = (event) => {
|
onLoad = (event) => {
|
||||||
console.log('HotPocketApp.onLoad()', event);
|
|
||||||
for (let pluginSpec of this.plugins) {
|
for (let pluginSpec of this.plugins) {
|
||||||
if (pluginSpec[1].onLoad) {
|
if (pluginSpec[1].onLoad) {
|
||||||
pluginSpec[1].onLoad(event);
|
pluginSpec[1].onLoad(event);
|
||||||
|
|||||||
@@ -130,6 +130,56 @@
|
|||||||
{% translate 'Log out' %}
|
{% translate 'Log out' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item px-3 d-flex justify-content-center">
|
||||||
|
{% spaceless %}
|
||||||
|
<a
|
||||||
|
class="btn btn-outline-info btn-sm"
|
||||||
|
href="https://apps.apple.com/pl/app/hotpocket-by-bthlabs/id6752321380"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="{% translate 'Safari and Share extension for iOS and macOS' %}"
|
||||||
|
>
|
||||||
|
<i class="bi bi-apple"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="btn btn-outline-info btn-sm ms-1"
|
||||||
|
href="https://chromewebstore.google.com/detail/save-to-hotpocket/mkmoejhhgnadmijpgkkioicjmikkkjbd"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="{% translate 'Chrome extension' %}"
|
||||||
|
>
|
||||||
|
<i class="bi bi-browser-chrome"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="btn btn-outline-info btn-sm ms-1"
|
||||||
|
href="https://addons.mozilla.org/en-GB/firefox/addon/save-to-hotpocket/"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="{% translate 'Firefox extension' %}"
|
||||||
|
>
|
||||||
|
<i class="bi bi-browser-firefox"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="btn btn-outline-info btn-sm ms-1"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#modalPWA"
|
||||||
|
href="#"
|
||||||
|
title="{% translate 'Android PWA' %}"
|
||||||
|
>
|
||||||
|
<i class="bi bi-android2"></i>
|
||||||
|
</a>
|
||||||
|
<div class="vr my-1 ms-1 opacity-75"></div>
|
||||||
|
<a
|
||||||
|
class="btn btn-outline-info btn-sm ms-1"
|
||||||
|
href="https://git.bthlabs.pl/tomekwojcik/hotpocket"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="{% translate 'Source code repository' %}"
|
||||||
|
>
|
||||||
|
<i class="bi bi-git"></i>
|
||||||
|
</a>
|
||||||
|
{% endspaceless %}
|
||||||
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'ui.accounts.login' %}">
|
<a class="nav-link" href="{% url 'ui.accounts.login' %}">
|
||||||
@@ -142,6 +192,38 @@
|
|||||||
{% include "ui/ui/partials/uname.html" %}
|
{% include "ui/ui/partials/uname.html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if not request.user.is_anonymous %}
|
||||||
|
<div class="modal modal-fade" id="modalPWA" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="modalPWALabel">
|
||||||
|
{% translate 'HotPocket on Android and others' %}
|
||||||
|
</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="mb-1">
|
||||||
|
{% blocktranslate %}
|
||||||
|
HotPocket doesn't natively support Android and other systems. However, it's a Progressive Web Application. You can install it from your browser and it'll register itself as share target.
|
||||||
|
{% endblocktranslate %}
|
||||||
|
</p>
|
||||||
|
<p class="mb-0">
|
||||||
|
{% blocktranslate %}
|
||||||
|
This is currently supported on Android and Windows 11 (when installed using Edge).
|
||||||
|
{% endblocktranslate %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">
|
||||||
|
{% translate 'Cool' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ def page_not_found(request: HttpRequest,
|
|||||||
exception: Exception,
|
exception: Exception,
|
||||||
template_name: str = ERROR_404_TEMPLATE_NAME,
|
template_name: str = ERROR_404_TEMPLATE_NAME,
|
||||||
) -> HttpResponseNotFound:
|
) -> HttpResponseNotFound:
|
||||||
if exception:
|
|
||||||
LOGGER.error('Exception: %s', exception, exc_info=exception)
|
|
||||||
|
|
||||||
return HttpResponseNotFound(render_to_string(
|
return HttpResponseNotFound(render_to_string(
|
||||||
'ui/errors/page_not_found.html',
|
'ui/errors/page_not_found.html',
|
||||||
context={},
|
context={},
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user