2020-07-28 03:26:45 +00:00
__package__ = ' archivebox.core '
2024-08-20 12:56:19 +00:00
import os
2021-02-16 20:47:49 +00:00
from pathlib import Path
2020-07-28 03:26:45 +00:00
2024-09-06 09:54:22 +00:00
from django . contrib import admin , messages
2024-08-27 03:16:43 +00:00
from django . urls import path , reverse , resolve
from django . utils import timezone
from django . utils . functional import cached_property
2020-06-30 09:56:17 +00:00
from django . utils . html import format_html
2020-07-28 09:59:54 +00:00
from django . utils . safestring import mark_safe
2024-09-10 07:05:01 +00:00
from django . contrib . auth import get_user_model , get_permission_codename
2024-09-04 00:11:10 +00:00
from django . contrib . auth . admin import UserAdmin
2024-08-27 03:16:43 +00:00
from django . core . paginator import Paginator
2024-08-20 08:57:07 +00:00
from django . core . exceptions import ValidationError
2024-09-05 06:42:55 +00:00
from django . template import Template , RequestContext
2024-08-21 01:31:21 +00:00
from django . conf import settings
2020-10-12 18:47:03 +00:00
from django import forms
2024-05-06 18:06:42 +00:00
2024-08-21 01:31:21 +00:00
from signal_webhooks . admin import WebhookAdmin
from signal_webhooks . utils import get_webhook_model
2024-09-30 22:59:05 +00:00
2024-10-01 00:44:18 +00:00
from archivebox . config import VERSION , DATA_DIR
2024-10-01 00:25:15 +00:00
from archivebox . misc . util import htmldecode , urldecode
2021-01-20 23:44:28 +00:00
2024-09-05 11:39:46 +00:00
from core . models import Snapshot , ArchiveResult , Tag
2020-11-17 23:42:57 +00:00
from core . mixins import SearchResultsAdminMixin
2024-04-25 10:56:22 +00:00
from api . models import APIToken
2024-08-21 02:26:40 +00:00
from abid_utils . admin import ABIDModelAdmin
2024-09-30 22:59:05 +00:00
from queues . tasks import bg_archive_links , bg_add
2024-10-03 10:10:22 +00:00
from machine . models import Machine , NetworkInterface , InstalledBinary
2020-11-17 23:42:57 +00:00
2020-11-28 07:12:27 +00:00
from index . html import snapshot_icons
2020-07-28 10:50:03 +00:00
from logging_util import printable_filesize
2024-09-25 12:10:09 +00:00
from main import remove
2020-07-28 10:50:03 +00:00
from extractors import archive_links
2024-01-09 04:55:30 +00:00
2024-05-18 03:13:54 +00:00
2024-10-01 06:21:34 +00:00
CONFIG = settings . FLAT_CONFIG
2024-08-21 01:31:21 +00:00
2024-09-30 22:59:05 +00:00
GLOBAL_CONTEXT = { ' VERSION ' : VERSION , ' VERSIONS_AVAILABLE ' : [ ] , ' CAN_UPGRADE ' : False }
2020-06-30 09:56:17 +00:00
2021-01-30 10:35:17 +00:00
# Admin URLs
# /admin/
# /admin/login/
# /admin/core/
# /admin/core/snapshot/
# /admin/core/snapshot/:uuid/
# /admin/core/tag/
# /admin/core/tag/:uuid/
2020-06-30 09:56:17 +00:00
# TODO: https://stackoverflow.com/questions/40760880/add-custom-button-to-django-admin-panel
2019-04-27 21:26:24 +00:00
2019-04-22 17:20:19 +00:00
2024-03-26 00:46:01 +00:00
class ArchiveBoxAdmin ( admin . AdminSite ) :
site_header = ' ArchiveBox '
index_title = ' Links '
site_title = ' Index '
namespace = ' admin '
2024-05-06 15:11:01 +00:00
2024-09-04 00:11:10 +00:00
class CustomUserAdmin ( UserAdmin ) :
sort_fields = [ ' id ' , ' email ' , ' username ' , ' is_superuser ' , ' last_login ' , ' date_joined ' ]
list_display = [ ' username ' , ' id ' , ' email ' , ' is_superuser ' , ' last_login ' , ' date_joined ' ]
readonly_fields = ( ' snapshot_set ' , ' archiveresult_set ' , ' tag_set ' , ' apitoken_set ' , ' outboundwebhook_set ' )
fieldsets = [ * UserAdmin . fieldsets , ( ' Data ' , { ' fields ' : readonly_fields } ) ]
@admin.display ( description = ' Snapshots ' )
def snapshot_set ( self , obj ) :
total_count = obj . snapshot_set . count ( )
return mark_safe ( ' <br/> ' . join (
format_html (
' <code><a href= " /admin/core/snapshot/ {} /change " ><b>[ {} ]</b></a></code> <b>📅 {} </b> {} ' ,
snap . pk ,
snap . abid ,
2024-09-05 06:42:36 +00:00
snap . downloaded_at . strftime ( ' % Y- % m- %d % H: % M ' ) if snap . downloaded_at else ' pending... ' ,
2024-09-04 00:11:10 +00:00
snap . url [ : 64 ] ,
)
2024-09-05 06:42:36 +00:00
for snap in obj . snapshot_set . order_by ( ' -modified_at ' ) [ : 10 ]
2024-09-04 00:11:10 +00:00
) + f ' <br/><a href= " /admin/core/snapshot/?created_by__id__exact= { obj . pk } " > { total_count } total records...<a> ' )
@admin.display ( description = ' Archive Result Logs ' )
def archiveresult_set ( self , obj ) :
total_count = obj . archiveresult_set . count ( )
return mark_safe ( ' <br/> ' . join (
format_html (
' <code><a href= " /admin/core/archiveresult/ {} /change " ><b>[ {} ]</b></a></code> <b>📅 {} </b> <b>📄 {} </b> {} ' ,
result . pk ,
result . abid ,
2024-09-05 06:42:36 +00:00
result . snapshot . downloaded_at . strftime ( ' % Y- % m- %d % H: % M ' ) if result . snapshot . downloaded_at else ' pending... ' ,
2024-09-04 00:11:10 +00:00
result . extractor ,
result . snapshot . url [ : 64 ] ,
)
2024-09-05 06:42:36 +00:00
for result in obj . archiveresult_set . order_by ( ' -modified_at ' ) [ : 10 ]
2024-09-04 00:11:10 +00:00
) + f ' <br/><a href= " /admin/core/archiveresult/?created_by__id__exact= { obj . pk } " > { total_count } total records...<a> ' )
@admin.display ( description = ' Tags ' )
def tag_set ( self , obj ) :
total_count = obj . tag_set . count ( )
return mark_safe ( ' , ' . join (
format_html (
' <code><a href= " /admin/core/tag/ {} /change " ><b> {} </b></a></code> ' ,
tag . pk ,
tag . name ,
)
2024-09-05 06:42:36 +00:00
for tag in obj . tag_set . order_by ( ' -modified_at ' ) [ : 10 ]
2024-09-04 00:11:10 +00:00
) + f ' <br/><a href= " /admin/core/tag/?created_by__id__exact= { obj . pk } " > { total_count } total records...<a> ' )
@admin.display ( description = ' API Tokens ' )
def apitoken_set ( self , obj ) :
total_count = obj . apitoken_set . count ( )
return mark_safe ( ' <br/> ' . join (
format_html (
' <code><a href= " /admin/api/apitoken/ {} /change " ><b>[ {} ]</b></a></code> {} (expires {} ) ' ,
apitoken . pk ,
apitoken . abid ,
apitoken . token_redacted [ : 64 ] ,
apitoken . expires ,
)
2024-09-05 06:42:36 +00:00
for apitoken in obj . apitoken_set . order_by ( ' -modified_at ' ) [ : 10 ]
2024-09-04 00:11:10 +00:00
) + f ' <br/><a href= " /admin/api/apitoken/?created_by__id__exact= { obj . pk } " > { total_count } total records...<a> ' )
@admin.display ( description = ' API Outbound Webhooks ' )
def outboundwebhook_set ( self , obj ) :
total_count = obj . outboundwebhook_set . count ( )
return mark_safe ( ' <br/> ' . join (
format_html (
' <code><a href= " /admin/api/outboundwebhook/ {} /change " ><b>[ {} ]</b></a></code> {} -> {} ' ,
outboundwebhook . pk ,
outboundwebhook . abid ,
outboundwebhook . referenced_model ,
outboundwebhook . endpoint ,
)
2024-09-05 06:42:36 +00:00
for outboundwebhook in obj . outboundwebhook_set . order_by ( ' -modified_at ' ) [ : 10 ]
2024-09-04 00:11:10 +00:00
) + f ' <br/><a href= " /admin/api/outboundwebhook/?created_by__id__exact= { obj . pk } " > { total_count } total records...<a> ' )
2024-09-10 07:04:39 +00:00
2024-03-26 00:46:01 +00:00
archivebox_admin = ArchiveBoxAdmin ( )
2024-09-04 00:11:10 +00:00
archivebox_admin . register ( get_user_model ( ) , CustomUserAdmin )
2024-03-26 00:46:01 +00:00
archivebox_admin . disable_action ( ' delete_selected ' )
2024-05-18 03:13:54 +00:00
# archivebox_admin.register(CustomPlugin)
2024-05-06 18:06:42 +00:00
2024-05-13 09:36:15 +00:00
# patch admin with methods to add data views (implemented by admin_data_views package)
2024-08-21 01:31:21 +00:00
# https://github.com/MrThearMan/django-admin-data-views
# https://mrthearman.github.io/django-admin-data-views/setup/
2024-05-18 03:13:54 +00:00
############### Additional sections are defined in settings.ADMIN_DATA_VIEWS #########
2024-05-06 18:06:42 +00:00
from admin_data_views . admin import get_app_list , admin_data_index_view , get_admin_data_urls , get_urls
archivebox_admin . get_app_list = get_app_list . __get__ ( archivebox_admin , ArchiveBoxAdmin )
2024-08-21 01:31:21 +00:00
archivebox_admin . admin_data_index_view = admin_data_index_view . __get__ ( archivebox_admin , ArchiveBoxAdmin ) # type: ignore
archivebox_admin . get_admin_data_urls = get_admin_data_urls . __get__ ( archivebox_admin , ArchiveBoxAdmin ) # type: ignore
2024-05-06 18:06:42 +00:00
archivebox_admin . get_urls = get_urls ( archivebox_admin . get_urls ) . __get__ ( archivebox_admin , ArchiveBoxAdmin )
2024-09-10 07:04:39 +00:00
from huey_monitor . apps import HueyMonitorConfig
HueyMonitorConfig . verbose_name = ' Background Workers '
from huey_monitor . admin import TaskModel , TaskModelAdmin , SignalInfoModel , SignalInfoModelAdmin
archivebox_admin . register ( SignalInfoModel , SignalInfoModelAdmin )
class CustomTaskModelAdmin ( TaskModelAdmin ) :
actions = [ " delete_selected " ]
def has_delete_permission ( self , request , obj = None ) :
codename = get_permission_codename ( " delete " , self . opts )
return request . user . has_perm ( " %s . %s " % ( self . opts . app_label , codename ) )
archivebox_admin . register ( TaskModel , CustomTaskModelAdmin )
def result_url ( result : TaskModel ) - > str :
url = reverse ( " admin:huey_monitor_taskmodel_change " , args = [ str ( result . id ) ] )
return format_html ( ' <a href= " {url} " class= " fade-in-progress-url " >See progress...</a> ' . format ( url = url ) )
2024-08-27 03:16:43 +00:00
class AccelleratedPaginator ( Paginator ) :
"""
Accellerated Pagniator ignores DISTINCT when counting total number of rows .
Speeds up SELECT Count ( * ) on Admin views by > 20 x .
https : / / hakibenita . com / optimizing - the - django - admin - paginator
"""
@cached_property
def count ( self ) :
2024-08-28 07:26:37 +00:00
if self . object_list . _has_filters ( ) : # type: ignore
2024-08-27 03:16:43 +00:00
# fallback to normal count method on filtered queryset
return super ( ) . count
else :
# otherwise count total rows in a separate fast query
return self . object_list . model . objects . count ( )
# Alternative approach for PostgreSQL: fallback count takes > 200ms
# from django.db import connection, transaction, OperationalError
# with transaction.atomic(), connection.cursor() as cursor:
# cursor.execute('SET LOCAL statement_timeout TO 200;')
# try:
# return super().count
# except OperationalError:
# return 9999999999999
2021-02-16 20:47:49 +00:00
class ArchiveResultInline ( admin . TabularInline ) :
2024-08-20 08:57:07 +00:00
name = ' Archive Results Log '
2021-02-16 20:47:49 +00:00
model = ArchiveResult
2024-08-27 03:16:43 +00:00
parent_model = Snapshot
2024-08-20 08:57:07 +00:00
# fk_name = 'snapshot'
2024-08-27 03:16:43 +00:00
extra = 0
sort_fields = ( ' end_ts ' , ' extractor ' , ' output ' , ' status ' , ' cmd_version ' )
2024-09-04 23:40:15 +00:00
readonly_fields = ( ' id ' , ' result_id ' , ' completed ' , ' command ' , ' version ' )
fields = ( ' start_ts ' , ' end_ts ' , * readonly_fields , ' extractor ' , ' cmd ' , ' cmd_version ' , ' pwd ' , ' created_by ' , ' status ' , ' output ' )
2024-08-27 03:16:43 +00:00
# exclude = ('id',)
ordering = ( ' end_ts ' , )
2024-08-20 08:57:07 +00:00
show_change_link = True
# # classes = ['collapse']
# # list_display_links = ['abid']
2024-08-27 03:16:43 +00:00
def get_parent_object_from_request ( self , request ) :
resolved = resolve ( request . path_info )
2024-09-05 06:42:55 +00:00
try :
return self . parent_model . objects . get ( pk = resolved . kwargs [ ' object_id ' ] )
except ( self . parent_model . DoesNotExist , ValidationError ) :
2024-09-05 10:36:18 +00:00
return self . parent_model . objects . get ( pk = self . parent_model . id_from_abid ( resolved . kwargs [ ' object_id ' ] ) )
2024-08-27 03:16:43 +00:00
@admin.display (
description = ' Completed ' ,
ordering = ' end_ts ' ,
)
def completed ( self , obj ) :
return format_html ( ' <p style= " white-space: nowrap " > {} </p> ' , obj . end_ts . strftime ( ' % Y- % m- %d % H: % M: % S ' ) )
2024-08-20 08:57:07 +00:00
def result_id ( self , obj ) :
2024-08-27 03:16:43 +00:00
return format_html ( ' <a href= " {} " ><code style= " font-size: 10px " >[ {} ]</code></a> ' , reverse ( ' admin:core_archiveresult_change ' , args = ( obj . id , ) ) , obj . abid )
2024-08-20 08:57:07 +00:00
def command ( self , obj ) :
return format_html ( ' <small><code> {} </code></small> ' , " " . join ( obj . cmd or [ ] ) )
2024-08-27 03:16:43 +00:00
def version ( self , obj ) :
return format_html ( ' <small><code> {} </code></small> ' , obj . cmd_version or ' - ' )
def get_formset ( self , request , obj = None , * * kwargs ) :
formset = super ( ) . get_formset ( request , obj , * * kwargs )
snapshot = self . get_parent_object_from_request ( request )
# import ipdb; ipdb.set_trace()
2024-09-04 23:40:15 +00:00
# formset.form.base_fields['id'].widget = formset.form.base_fields['id'].hidden_widget()
2024-08-27 03:16:43 +00:00
# default values for new entries
formset . form . base_fields [ ' status ' ] . initial = ' succeeded '
formset . form . base_fields [ ' start_ts ' ] . initial = timezone . now ( )
formset . form . base_fields [ ' end_ts ' ] . initial = timezone . now ( )
formset . form . base_fields [ ' cmd_version ' ] . initial = ' - '
formset . form . base_fields [ ' pwd ' ] . initial = str ( snapshot . link_dir )
formset . form . base_fields [ ' created_by ' ] . initial = request . user
formset . form . base_fields [ ' cmd ' ] = forms . JSONField ( initial = [ ' - ' ] )
formset . form . base_fields [ ' output ' ] . initial = ' Manually recorded cmd output... '
if obj is not None :
# hidden values for existing entries and new entries
formset . form . base_fields [ ' start_ts ' ] . widget = formset . form . base_fields [ ' start_ts ' ] . hidden_widget ( )
formset . form . base_fields [ ' end_ts ' ] . widget = formset . form . base_fields [ ' end_ts ' ] . hidden_widget ( )
formset . form . base_fields [ ' cmd ' ] . widget = formset . form . base_fields [ ' cmd ' ] . hidden_widget ( )
formset . form . base_fields [ ' pwd ' ] . widget = formset . form . base_fields [ ' pwd ' ] . hidden_widget ( )
formset . form . base_fields [ ' created_by ' ] . widget = formset . form . base_fields [ ' created_by ' ] . hidden_widget ( )
formset . form . base_fields [ ' cmd_version ' ] . widget = formset . form . base_fields [ ' cmd_version ' ] . hidden_widget ( )
return formset
def get_readonly_fields ( self , request , obj = None ) :
if obj is not None :
return self . readonly_fields
else :
return [ ]
2024-08-20 01:34:01 +00:00
2024-08-20 08:57:07 +00:00
class TagInline ( admin . TabularInline ) :
2024-08-21 01:31:21 +00:00
model = Tag . snapshot_set . through # type: ignore
2024-08-20 01:34:01 +00:00
# fk_name = 'snapshot'
fields = ( ' id ' , ' tag ' )
extra = 1
# min_num = 1
max_num = 1000
autocomplete_fields = (
' tag ' ,
)
2021-02-18 13:04:14 +00:00
2021-02-18 07:32:58 +00:00
from django . contrib . admin . helpers import ActionForm
2024-08-20 10:29:57 +00:00
from django . contrib . admin . widgets import FilteredSelectMultiple
2021-04-10 08:11:32 +00:00
2024-08-20 10:29:57 +00:00
# class AutocompleteTags:
# model = Tag
# search_fields = ['name']
# name = 'name'
# # source_field = 'name'
# remote_field = Tag._meta.get_field('name')
2021-04-10 08:11:32 +00:00
2024-08-20 10:29:57 +00:00
# class AutocompleteTagsAdminStub:
# name = 'admin'
2021-02-18 07:32:58 +00:00
class SnapshotActionForm ( ActionForm ) :
2021-04-10 08:11:32 +00:00
tags = forms . ModelMultipleChoiceField (
2024-09-02 08:14:06 +00:00
label = ' Edit tags ' ,
2021-04-10 08:11:32 +00:00
queryset = Tag . objects . all ( ) ,
required = False ,
2024-08-20 10:29:57 +00:00
widget = FilteredSelectMultiple (
' core_tag__name ' ,
False ,
2024-01-04 03:00:07 +00:00
) ,
2021-04-10 08:11:32 +00:00
)
# TODO: allow selecting actions for specific extractors? is this useful?
# extractor = forms.ChoiceField(
2024-08-22 22:36:02 +00:00
# choices=ArchiveResult.EXTRACTOR_CHOICES,
2021-04-10 08:11:32 +00:00
# required=False,
# widget=forms.MultileChoiceField(attrs={'class': "form-control"})
# )
2021-02-18 07:32:58 +00:00
2020-10-12 18:47:03 +00:00
2024-09-03 21:16:44 +00:00
2024-05-13 14:50:07 +00:00
2024-03-26 00:46:01 +00:00
@admin.register ( Snapshot , site = archivebox_admin )
2024-08-21 02:26:40 +00:00
class SnapshotAdmin ( SearchResultsAdminMixin , ABIDModelAdmin ) :
2024-09-05 06:42:36 +00:00
list_display = ( ' created_at ' , ' title_str ' , ' files ' , ' size ' , ' url_str ' )
sort_fields = ( ' title_str ' , ' url_str ' , ' created_at ' )
readonly_fields = ( ' admin_actions ' , ' status_info ' , ' tags_str ' , ' imported_timestamp ' , ' created_at ' , ' modified_at ' , ' downloaded_at ' , ' abid_info ' , ' link_dir ' )
2024-09-04 23:40:15 +00:00
search_fields = ( ' id ' , ' url ' , ' abid ' , ' timestamp ' , ' title ' , ' tags__name ' )
2024-09-05 06:42:36 +00:00
list_filter = ( ' created_at ' , ' downloaded_at ' , ' archiveresult__status ' , ' created_by ' , ' tags__name ' )
fields = ( ' url ' , ' title ' , ' created_by ' , ' bookmarked_at ' , * readonly_fields )
ordering = [ ' -created_at ' ]
2021-04-10 08:11:32 +00:00
actions = [ ' add_tags ' , ' remove_tags ' , ' update_titles ' , ' update_snapshots ' , ' resnapshot_snapshot ' , ' overwrite_snapshots ' , ' delete_snapshots ' ]
2024-08-20 08:57:07 +00:00
inlines = [ TagInline , ArchiveResultInline ]
2024-08-27 03:16:43 +00:00
list_per_page = min ( max ( 5 , CONFIG . SNAPSHOTS_PER_PAGE ) , 5000 )
2020-06-30 09:56:17 +00:00
2021-02-18 07:32:58 +00:00
action_form = SnapshotActionForm
2024-08-27 03:16:43 +00:00
paginator = AccelleratedPaginator
2021-02-18 07:32:58 +00:00
2024-08-20 01:34:01 +00:00
save_on_top = True
2024-08-27 03:16:43 +00:00
show_full_result_count = False
2024-08-20 01:34:01 +00:00
2024-01-09 04:55:30 +00:00
def changelist_view ( self , request , extra_context = None ) :
2024-09-03 21:16:44 +00:00
self . request = request
2024-01-09 04:55:30 +00:00
extra_context = extra_context or { }
2024-08-20 01:34:01 +00:00
try :
return super ( ) . changelist_view ( request , extra_context | GLOBAL_CONTEXT )
except Exception as e :
self . message_user ( request , f ' Error occurred while loading the page: { str ( e ) } { request . GET } { request . POST } ' )
return super ( ) . changelist_view ( request , GLOBAL_CONTEXT )
2024-01-09 04:55:30 +00:00
2024-08-20 08:57:07 +00:00
2020-12-12 04:03:46 +00:00
def get_urls ( self ) :
urls = super ( ) . get_urls ( )
custom_urls = [
2021-04-10 08:12:30 +00:00
path ( ' grid/ ' , self . admin_site . admin_view ( self . grid_view ) , name = ' grid ' )
2020-12-12 04:03:46 +00:00
]
return custom_urls + urls
2024-08-27 03:16:43 +00:00
# def get_queryset(self, request):
# # tags_qs = SnapshotTag.objects.all().select_related('tag')
# # prefetch = Prefetch('snapshottag_set', queryset=tags_qs)
# self.request = request
# return super().get_queryset(request).prefetch_related('archiveresult_set').distinct() # .annotate(archiveresult_count=Count('archiveresult'))
2020-09-21 16:50:26 +00:00
2024-09-05 06:42:36 +00:00
@admin.action (
description = " Imported Timestamp "
)
def imported_timestamp ( self , obj ) :
context = RequestContext ( self . request , {
' bookmarked_date ' : obj . bookmarked ,
' timestamp ' : obj . timestamp ,
} )
html = Template ( """ {{ bookmarked_date}} (<code> {{ timestamp}}</code>) """ )
return mark_safe ( html . render ( context ) )
# pretty_time = obj.bookmarked.strftime('%Y-%m-%d %H:%M:%S')
# return f'{pretty_time} ({obj.timestamp})'
2020-09-21 16:50:26 +00:00
2021-02-18 07:32:58 +00:00
# TODO: figure out a different way to do this, you cant nest forms so this doenst work
# def action(self, obj):
# # csrfmiddlewaretoken: Wa8UcQ4fD3FJibzxqHN3IYrrjLo4VguWynmbzzcPYoebfVUnDovon7GEMYFRgsh0
# # action: update_snapshots
# # select_across: 0
# # _selected_action: 76d29b26-2a88-439e-877c-a7cca1b72bb3
# return format_html(
# '''
# <form action="/admin/core/snapshot/" method="post" onsubmit="e => e.stopPropagation()">
# <input type="hidden" name="csrfmiddlewaretoken" value="{}">
# <input type="hidden" name="_selected_action" value="{}">
# <button name="update_snapshots">Check</button>
# <button name="update_titles">Pull title + favicon</button>
# <button name="update_snapshots">Update</button>
# <button name="overwrite_snapshots">Re-Archive (overwrite)</button>
# <button name="delete_snapshots">Permanently delete</button>
# </form>
# ''',
# csrf.get_token(self.request),
2024-05-13 12:12:12 +00:00
# obj.pk,
2021-02-18 07:32:58 +00:00
# )
2024-05-13 14:50:07 +00:00
def admin_actions ( self , obj ) :
2020-06-30 09:56:17 +00:00
return format_html (
2024-05-13 14:50:07 +00:00
# URL Hash: <code style="font-size: 10px; user-select: all">{}</code><br/>
'''
2024-05-18 03:11:31 +00:00
< a class = " btn " style = " font-size: 18px; display: inline-block; border-radius: 10px; border: 3px solid #eee; padding: 4px 8px " href = " /archive/ {} " > Summary page ➡ ️ < / a > & nbsp ; & nbsp ;
< a class = " btn " style = " font-size: 18px; display: inline-block; border-radius: 10px; border: 3px solid #eee; padding: 4px 8px " href = " /archive/ {} /index.html#all " > Result files 📑 < / a > & nbsp ; & nbsp ;
< a class = " btn " style = " font-size: 18px; display: inline-block; border-radius: 10px; border: 3px solid #eee; padding: 4px 8px " href = " /admin/core/snapshot/?id__exact= {} " > Admin actions ⚙ ️ < / a >
2024-05-13 14:50:07 +00:00
''' ,
obj . timestamp ,
obj . timestamp ,
obj . pk ,
)
def status_info ( self , obj ) :
return format_html (
# URL Hash: <code style="font-size: 10px; user-select: all">{}</code><br/>
2021-04-10 08:12:30 +00:00
'''
Archived : { } ( { } files { } ) & nbsp ; & nbsp ;
Favicon : < img src = " {} " style = " height: 20px " / > & nbsp ; & nbsp ;
2024-05-13 14:50:07 +00:00
Status code : { } & nbsp ; & nbsp ; < br / >
2021-04-10 08:12:30 +00:00
Server : { } & nbsp ; & nbsp ;
Content type : { } & nbsp ; & nbsp ;
Extension : { } & nbsp ; & nbsp ;
''' ,
' ✅ ' if obj . is_archived else ' ❌ ' ,
obj . num_outputs ,
2024-05-18 03:11:31 +00:00
self . size ( obj ) or ' 0kb ' ,
2021-04-10 08:12:30 +00:00
f ' /archive/ { obj . timestamp } /favicon.ico ' ,
2024-05-18 03:11:31 +00:00
obj . status_code or ' - ' ,
obj . headers and obj . headers . get ( ' Server ' ) or ' - ' ,
obj . headers and obj . headers . get ( ' Content-Type ' ) or ' - ' ,
obj . extension or ' - ' ,
2020-06-30 09:56:17 +00:00
)
2024-03-26 00:46:01 +00:00
@admin.display (
description = ' Title ' ,
ordering = ' title ' ,
)
2020-06-30 09:56:17 +00:00
def title_str ( self , obj ) :
2020-07-28 09:59:54 +00:00
tags = ' ' . join (
2024-08-27 03:16:43 +00:00
format_html ( ' <a href= " /admin/core/snapshot/?tags__id__exact= {} " ><span class= " tag " > {} </span></a> ' , tag . pk , tag . name )
2020-09-21 16:50:26 +00:00
for tag in obj . tags . all ( )
2024-08-27 03:16:43 +00:00
if str ( tag . name ) . strip ( )
2020-10-07 15:15:56 +00:00
)
2020-06-30 09:56:17 +00:00
return format_html (
' <a href= " / {} " > '
2024-08-27 03:16:43 +00:00
' <img src= " / {} /favicon.ico " class= " favicon " onerror= " this.remove() " > '
2020-06-30 09:56:17 +00:00
' </a> '
2020-08-18 13:17:21 +00:00
' <a href= " / {} /index.html " > '
2020-07-28 03:26:45 +00:00
' <b class= " status- {} " > {} </b> '
' </a> ' ,
2020-06-30 09:56:17 +00:00
obj . archive_path ,
2024-08-27 03:16:43 +00:00
obj . archive_path ,
2020-08-18 13:17:21 +00:00
obj . archive_path ,
2020-07-28 03:26:45 +00:00
' fetched ' if obj . latest_title or obj . title else ' pending ' ,
2020-07-28 09:59:54 +00:00
urldecode ( htmldecode ( obj . latest_title or obj . title or ' ' ) ) [ : 128 ] or ' Pending... '
2020-10-31 11:55:27 +00:00
) + mark_safe ( f ' <span class= " tags " > { tags } </span> ' )
2020-06-30 09:56:17 +00:00
2024-03-26 00:46:01 +00:00
@admin.display (
description = ' Files Saved ' ,
2024-08-27 03:16:43 +00:00
# ordering='archiveresult_count',
2024-03-26 00:46:01 +00:00
)
2020-06-30 09:56:17 +00:00
def files ( self , obj ) :
2024-09-04 07:08:14 +00:00
# return '-'
2020-11-28 07:12:27 +00:00
return snapshot_icons ( obj )
2020-07-27 23:29:57 +00:00
2021-04-10 08:12:30 +00:00
2024-03-26 00:46:01 +00:00
@admin.display (
2024-08-27 03:16:43 +00:00
# ordering='archiveresult_count'
2024-03-26 00:46:01 +00:00
)
2020-07-27 23:29:57 +00:00
def size ( self , obj ) :
2024-10-08 10:02:34 +00:00
archive_size = os . access ( Path ( obj . link_dir ) / ' index.html ' , os . F_OK ) and obj . archive_size
2020-10-31 11:56:19 +00:00
if archive_size :
size_txt = printable_filesize ( archive_size )
if archive_size > 52428800 :
size_txt = mark_safe ( f ' <b> { size_txt } </b> ' )
else :
2020-10-31 23:32:43 +00:00
size_txt = mark_safe ( ' <span style= " opacity: 0.3 " >...</span> ' )
2020-07-27 23:29:57 +00:00
return format_html (
' <a href= " / {} " title= " View all files " > {} </a> ' ,
2020-06-30 09:56:17 +00:00
obj . archive_path ,
2020-10-31 11:56:19 +00:00
size_txt ,
2020-06-30 09:56:17 +00:00
)
2021-04-10 08:12:30 +00:00
2024-03-26 00:46:01 +00:00
@admin.display (
description = ' Original URL ' ,
ordering = ' url ' ,
)
2020-06-30 09:56:17 +00:00
def url_str ( self , obj ) :
return format_html (
2021-04-10 08:12:30 +00:00
' <a href= " {} " ><code style= " user-select: all; " > {} </code></a> ' ,
obj . url ,
2024-05-13 14:50:07 +00:00
obj . url [ : 128 ] ,
2020-06-30 09:56:17 +00:00
)
2019-04-22 17:20:19 +00:00
2021-04-10 08:12:30 +00:00
def grid_view ( self , request , extra_context = None ) :
2020-12-12 04:03:46 +00:00
# cl = self.get_changelist_instance(request)
# Save before monkey patching to restore for changelist list view
saved_change_list_template = self . change_list_template
saved_list_per_page = self . list_per_page
saved_list_max_show_all = self . list_max_show_all
# Monkey patch here plus core_tags.py
2021-01-30 10:35:07 +00:00
self . change_list_template = ' private_index_grid.html '
2024-08-21 01:31:21 +00:00
self . list_per_page = CONFIG . SNAPSHOTS_PER_PAGE
2020-12-12 04:03:46 +00:00
self . list_max_show_all = self . list_per_page
# Call monkey patched view
2021-04-10 08:12:30 +00:00
rendered_response = self . changelist_view ( request , extra_context = extra_context )
2020-12-12 04:03:46 +00:00
# Restore values
2021-03-27 09:12:54 +00:00
self . change_list_template = saved_change_list_template
2020-12-12 04:03:46 +00:00
self . list_per_page = saved_list_per_page
self . list_max_show_all = saved_list_max_show_all
return rendered_response
2021-02-18 07:32:58 +00:00
2021-04-10 08:12:30 +00:00
# for debugging, uncomment this to print all requests:
# def changelist_view(self, request, extra_context=None):
# print('[*] Got request', request.method, request.POST)
# return super().changelist_view(request, extra_context=None)
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
2024-09-02 08:14:06 +00:00
description = " ℹ ️ Get Title"
2024-03-26 00:46:01 +00:00
)
2024-09-02 08:14:06 +00:00
def update_titles ( self , request , queryset ) :
2024-09-06 09:54:22 +00:00
links = [ snapshot . as_link ( ) for snapshot in queryset ]
if len ( links ) < 3 :
# run syncronously if there are only 1 or 2 links
2024-10-01 00:44:18 +00:00
archive_links ( links , overwrite = True , methods = ( ' title ' , ' favicon ' ) , out_dir = DATA_DIR )
2024-09-06 09:54:22 +00:00
messages . success ( request , f " Title and favicon have been fetched and saved for { len ( links ) } URLs. " )
else :
2024-09-10 07:04:39 +00:00
# otherwise run in a background worker
2024-10-01 00:44:18 +00:00
result = bg_archive_links ( ( links , ) , kwargs = { " overwrite " : True , " methods " : [ " title " , " favicon " ] , " out_dir " : DATA_DIR } )
2024-09-10 07:04:39 +00:00
messages . success (
request ,
mark_safe ( f " Title and favicon are updating in the background for { len ( links ) } URLs. { result_url ( result ) } " ) ,
2024-09-06 09:54:22 +00:00
)
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
2024-09-02 08:14:06 +00:00
description = " ⬇️ Get Missing "
2024-03-26 00:46:01 +00:00
)
2024-09-02 08:14:06 +00:00
def update_snapshots ( self , request , queryset ) :
2024-09-06 09:54:22 +00:00
links = [ snapshot . as_link ( ) for snapshot in queryset ]
2024-09-10 07:04:39 +00:00
2024-10-01 00:44:18 +00:00
result = bg_archive_links ( ( links , ) , kwargs = { " overwrite " : False , " out_dir " : DATA_DIR } )
2024-09-10 07:04:39 +00:00
2024-09-06 09:54:22 +00:00
messages . success (
2024-09-10 07:04:39 +00:00
request ,
mark_safe ( f " Re-trying any previously failed methods for { len ( links ) } URLs in the background. { result_url ( result ) } " ) ,
2024-09-06 09:54:22 +00:00
)
2024-09-02 08:14:06 +00:00
2021-04-10 08:12:30 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
2024-09-10 07:04:39 +00:00
description = " 🆕 Archive Again "
2024-03-26 00:46:01 +00:00
)
2021-04-10 08:12:30 +00:00
def resnapshot_snapshot ( self , request , queryset ) :
for snapshot in queryset :
2024-09-06 09:54:22 +00:00
timestamp = timezone . now ( ) . isoformat ( ' T ' , ' seconds ' )
2021-04-10 08:12:30 +00:00
new_url = snapshot . url . split ( ' # ' ) [ 0 ] + f ' # { timestamp } '
2024-09-06 09:54:22 +00:00
2024-09-10 07:04:39 +00:00
result = bg_add ( { ' urls ' : new_url , ' tag ' : snapshot . tags_str ( ) } )
2024-09-06 09:54:22 +00:00
messages . success (
request ,
2024-09-10 07:04:39 +00:00
mark_safe ( f " Creating new fresh snapshots for { queryset . count ( ) } URLs in the background. { result_url ( result ) } " ) ,
2024-09-06 09:54:22 +00:00
)
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
2024-09-10 07:04:39 +00:00
description = " 🔄 Redo "
2024-03-26 00:46:01 +00:00
)
2021-02-18 09:29:24 +00:00
def overwrite_snapshots ( self , request , queryset ) :
2024-09-06 09:54:22 +00:00
links = [ snapshot . as_link ( ) for snapshot in queryset ]
2024-09-10 07:04:39 +00:00
2024-10-01 00:44:18 +00:00
result = bg_archive_links ( ( links , ) , kwargs = { " overwrite " : True , " out_dir " : DATA_DIR } )
2024-09-10 07:04:39 +00:00
2024-09-06 09:54:22 +00:00
messages . success (
request ,
2024-09-10 07:04:39 +00:00
mark_safe ( f " Clearing all previous results and re-downloading { len ( links ) } URLs in the background. { result_url ( result ) } " ) ,
2024-09-06 09:54:22 +00:00
)
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
2024-09-02 08:14:06 +00:00
description = " ☠️ Delete "
2024-03-26 00:46:01 +00:00
)
2021-02-18 09:29:24 +00:00
def delete_snapshots ( self , request , queryset ) :
2024-10-01 00:44:18 +00:00
remove ( snapshots = queryset , yes = True , delete = True , out_dir = DATA_DIR )
2024-09-06 09:54:22 +00:00
messages . success (
request ,
2024-09-10 07:04:39 +00:00
mark_safe ( f " Succesfully deleted { queryset . count ( ) } Snapshots. Don ' t forget to scrub URLs from import logs (data/sources) and error logs (data/logs) if needed. " ) ,
2024-09-06 09:54:22 +00:00
)
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
description = " + "
)
2021-04-10 08:11:32 +00:00
def add_tags ( self , request , queryset ) :
tags = request . POST . getlist ( ' tags ' )
print ( ' [+] Adding tags ' , tags , ' to Snapshots ' , queryset )
2021-03-27 09:27:02 +00:00
for obj in queryset :
2021-04-10 08:11:32 +00:00
obj . tags . add ( * tags )
2024-09-06 09:54:22 +00:00
messages . success (
request ,
2024-09-10 07:04:39 +00:00
f " Added { len ( tags ) } tags to { queryset . count ( ) } Snapshots. " ,
2024-09-06 09:54:22 +00:00
)
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.action (
description = " – "
)
2021-04-10 08:11:32 +00:00
def remove_tags ( self , request , queryset ) :
tags = request . POST . getlist ( ' tags ' )
print ( ' [-] Removing tags ' , tags , ' to Snapshots ' , queryset )
2021-02-18 07:32:58 +00:00
for obj in queryset :
2021-04-10 08:11:32 +00:00
obj . tags . remove ( * tags )
2024-09-06 09:54:22 +00:00
messages . success (
request ,
2024-09-10 07:04:39 +00:00
f " Removed { len ( tags ) } tags from { queryset . count ( ) } Snapshots. " ,
2024-09-06 09:54:22 +00:00
)
2019-04-22 17:20:19 +00:00
2019-04-23 01:40:42 +00:00
2024-08-18 07:24:38 +00:00
# @admin.register(SnapshotTag, site=archivebox_admin)
2024-08-21 02:26:40 +00:00
# class SnapshotTagAdmin(ABIDModelAdmin):
2024-08-18 07:24:38 +00:00
# list_display = ('id', 'snapshot', 'tag')
# sort_fields = ('id', 'snapshot', 'tag')
# search_fields = ('id', 'snapshot_id', 'tag_id')
# fields = ('snapshot', 'id')
# actions = ['delete_selected']
# ordering = ['-id']
2021-02-18 07:32:58 +00:00
2024-03-26 00:46:01 +00:00
@admin.register ( Tag , site = archivebox_admin )
2024-08-21 02:26:40 +00:00
class TagAdmin ( ABIDModelAdmin ) :
2024-09-05 06:42:36 +00:00
list_display = ( ' created_at ' , ' created_by ' , ' abid ' , ' name ' , ' num_snapshots ' , ' snapshots ' )
list_filter = ( ' created_at ' , ' created_by ' )
sort_fields = ( ' name ' , ' slug ' , ' abid ' , ' created_by ' , ' created_at ' )
readonly_fields = ( ' slug ' , ' abid ' , ' created_at ' , ' modified_at ' , ' abid_info ' , ' snapshots ' )
2024-08-20 08:57:07 +00:00
search_fields = ( ' abid ' , ' name ' , ' slug ' )
fields = ( ' name ' , ' created_by ' , * readonly_fields )
2021-02-18 07:32:58 +00:00
actions = [ ' delete_selected ' ]
2024-09-05 06:42:36 +00:00
ordering = [ ' -created_at ' ]
2021-02-18 07:32:58 +00:00
2024-08-27 03:16:43 +00:00
paginator = AccelleratedPaginator
2024-05-13 14:50:07 +00:00
2024-05-13 12:12:12 +00:00
def num_snapshots ( self , tag ) :
2021-02-18 07:32:58 +00:00
return format_html (
' <a href= " /admin/core/snapshot/?tags__id__exact= {} " > {} total</a> ' ,
2024-05-13 12:12:12 +00:00
tag . id ,
tag . snapshot_set . count ( ) ,
2021-02-18 07:32:58 +00:00
)
2024-05-13 12:12:12 +00:00
def snapshots ( self , tag ) :
total_count = tag . snapshot_set . count ( )
2021-02-18 07:32:58 +00:00
return mark_safe ( ' <br/> ' . join (
format_html (
2024-08-20 08:57:07 +00:00
' <code><a href= " /admin/core/snapshot/ {} /change " ><b>[ {} ]</b></a></code> {} ' ,
2024-05-13 12:12:12 +00:00
snap . pk ,
2024-09-05 06:42:36 +00:00
snap . downloaded_at . strftime ( ' % Y- % m- %d % H: % M ' ) if snap . downloaded_at else ' pending... ' ,
2024-08-20 08:57:07 +00:00
snap . url [ : 64 ] ,
2021-02-18 07:32:58 +00:00
)
2024-09-05 06:42:36 +00:00
for snap in tag . snapshot_set . order_by ( ' -downloaded_at ' ) [ : 10 ]
2024-09-04 00:11:10 +00:00
) + ( f ' <br/><a href= " /admin/core/snapshot/?tags__id__exact= { tag . id } " > { total_count } total snapshots...<a> ' ) )
2021-02-18 07:32:58 +00:00
2020-07-28 03:26:45 +00:00
2024-03-26 00:46:01 +00:00
@admin.register ( ArchiveResult , site = archivebox_admin )
2024-08-21 02:26:40 +00:00
class ArchiveResultAdmin ( ABIDModelAdmin ) :
2024-05-13 14:50:07 +00:00
list_display = ( ' start_ts ' , ' snapshot_info ' , ' tags_str ' , ' extractor ' , ' cmd_str ' , ' status ' , ' output_str ' )
2021-02-16 11:13:27 +00:00
sort_fields = ( ' start_ts ' , ' extractor ' , ' status ' )
2024-09-05 06:42:36 +00:00
readonly_fields = ( ' cmd_str ' , ' snapshot_info ' , ' tags_str ' , ' created_at ' , ' modified_at ' , ' abid_info ' , ' output_summary ' )
2024-09-04 23:40:15 +00:00
search_fields = ( ' id ' , ' abid ' , ' snapshot__url ' , ' extractor ' , ' output ' , ' cmd_version ' , ' cmd ' , ' snapshot__timestamp ' )
2024-08-20 12:56:19 +00:00
fields = ( ' snapshot ' , ' extractor ' , ' status ' , ' output ' , ' pwd ' , ' start_ts ' , ' end_ts ' , ' created_by ' , ' cmd_version ' , ' cmd ' , * readonly_fields )
2021-02-18 13:04:14 +00:00
autocomplete_fields = [ ' snapshot ' ]
2021-02-16 20:47:49 +00:00
list_filter = ( ' status ' , ' extractor ' , ' start_ts ' , ' cmd_version ' )
ordering = [ ' -start_ts ' ]
2024-08-21 01:31:21 +00:00
list_per_page = CONFIG . SNAPSHOTS_PER_PAGE
2024-08-27 03:16:43 +00:00
paginator = AccelleratedPaginator
2024-09-05 10:36:18 +00:00
save_on_top = True
2024-09-10 10:09:43 +00:00
actions = [ ' delete_selected ' ]
class Meta :
verbose_name = ' Archive Result '
verbose_name_plural = ' Archive Results '
2021-02-16 20:47:49 +00:00
2024-09-03 21:16:44 +00:00
def change_view ( self , request , object_id , form_url = " " , extra_context = None ) :
self . request = request
return super ( ) . change_view ( request , object_id , form_url , extra_context )
2024-03-26 00:46:01 +00:00
@admin.display (
2024-05-13 14:50:07 +00:00
description = ' Snapshot Info '
2024-03-26 00:46:01 +00:00
)
2024-05-13 14:50:07 +00:00
def snapshot_info ( self , result ) :
2021-02-16 20:47:49 +00:00
return format_html (
2024-05-13 14:50:07 +00:00
' <a href= " /archive/ {} /index.html " ><b><code>[ {} ]</code></b> {} {} </a><br/> ' ,
2024-05-13 12:12:12 +00:00
result . snapshot . timestamp ,
2024-05-13 14:50:07 +00:00
result . snapshot . abid ,
2024-09-05 06:42:36 +00:00
result . snapshot . bookmarked_at . strftime ( ' % Y- % m- %d % H: % M ' ) ,
2024-05-13 12:12:12 +00:00
result . snapshot . url [ : 128 ] ,
2021-02-16 20:47:49 +00:00
)
2024-05-13 14:50:07 +00:00
2024-03-26 00:46:01 +00:00
@admin.display (
2024-05-13 14:50:07 +00:00
description = ' Snapshot Tags '
2024-03-26 00:46:01 +00:00
)
2024-05-13 12:12:12 +00:00
def tags_str ( self , result ) :
return result . snapshot . tags_str ( )
2021-04-01 05:39:34 +00:00
2024-05-13 12:12:12 +00:00
def cmd_str ( self , result ) :
2021-02-16 20:47:49 +00:00
return format_html (
' <pre> {} </pre> ' ,
2024-05-13 12:12:12 +00:00
' ' . join ( result . cmd ) if isinstance ( result . cmd , list ) else str ( result . cmd ) ,
2021-02-16 20:47:49 +00:00
)
2024-08-20 12:56:19 +00:00
2024-05-13 12:12:12 +00:00
def output_str ( self , result ) :
2021-02-16 20:47:49 +00:00
return format_html (
2021-02-18 07:32:58 +00:00
' <a href= " /archive/ {} / {} " class= " output-link " >↗️</a><pre> {} </pre> ' ,
2024-05-13 12:12:12 +00:00
result . snapshot . timestamp ,
result . output if ( result . status == ' succeeded ' ) and result . extractor not in ( ' title ' , ' archive_org ' ) else ' index.html ' ,
result . output ,
2021-02-16 20:47:49 +00:00
)
2024-08-20 12:56:19 +00:00
def output_summary ( self , result ) :
2024-10-01 00:44:18 +00:00
snapshot_dir = Path ( DATA_DIR ) / str ( result . pwd ) . split ( ' data/ ' , 1 ) [ - 1 ]
2024-08-20 12:56:19 +00:00
output_str = format_html (
' <pre style= " display: inline-block " > {} </pre><br/> ' ,
result . output ,
)
output_str + = format_html ( ' <a href= " /archive/ {} /index.html#all " >See result files ...</a><br/><pre><code> ' , str ( result . snapshot . timestamp ) )
path_from_output_str = ( snapshot_dir / result . output )
output_str + = format_html ( ' <i style= " padding: 1px " > {} </i><b style= " padding-right: 20px " >/</b><i> {} </i><br/><hr/> ' , str ( snapshot_dir ) , str ( result . output ) )
2024-10-08 10:02:34 +00:00
if os . access ( path_from_output_str , os . R_OK ) :
2024-08-20 12:56:19 +00:00
root_dir = str ( path_from_output_str )
else :
root_dir = str ( snapshot_dir )
# print(root_dir, str(list(os.walk(root_dir))))
for root , dirs , files in os . walk ( root_dir ) :
depth = root . replace ( root_dir , ' ' ) . count ( os . sep ) + 1
if depth > 2 :
continue
indent = ' ' * 4 * ( depth )
output_str + = format_html ( ' <b style= " padding: 1px " > {} {} /</b><br/> ' , indent , os . path . basename ( root ) )
indentation_str = ' ' * 4 * ( depth + 1 )
for filename in sorted ( files ) :
is_hidden = filename . startswith ( ' . ' )
output_str + = format_html ( ' <span style= " opacity: {} .2 " > {} {} </span><br/> ' , int ( not is_hidden ) , indentation_str , filename . strip ( ) )
return output_str + format_html ( ' </code></pre> ' )
2024-08-21 02:26:40 +00:00
@admin.register ( APIToken , site = archivebox_admin )
class APITokenAdmin ( ABIDModelAdmin ) :
2024-09-05 06:42:36 +00:00
list_display = ( ' created_at ' , ' abid ' , ' created_by ' , ' token_redacted ' , ' expires ' )
sort_fields = ( ' abid ' , ' created_at ' , ' created_by ' , ' expires ' )
readonly_fields = ( ' created_at ' , ' modified_at ' , ' abid_info ' )
2024-08-21 02:26:40 +00:00
search_fields = ( ' id ' , ' abid ' , ' created_by__username ' , ' token ' )
fields = ( ' created_by ' , ' token ' , ' expires ' , * readonly_fields )
list_filter = ( ' created_by ' , )
2024-09-05 06:42:36 +00:00
ordering = [ ' -created_at ' ]
2024-08-21 02:26:40 +00:00
list_per_page = 100
@admin.register ( get_webhook_model ( ) , site = archivebox_admin )
class CustomWebhookAdmin ( WebhookAdmin , ABIDModelAdmin ) :
2024-09-05 06:42:36 +00:00
list_display = ( ' created_at ' , ' created_by ' , ' abid ' , * WebhookAdmin . list_display )
sort_fields = ( ' created_at ' , ' created_by ' , ' abid ' , ' referenced_model ' , ' endpoint ' , ' last_success ' , ' last_error ' )
readonly_fields = ( ' created_at ' , ' modified_at ' , ' abid_info ' , * WebhookAdmin . readonly_fields )
2024-10-02 04:46:35 +00:00
@admin.register ( Machine , site = archivebox_admin )
class MachineAdmin ( ABIDModelAdmin ) :
2024-10-03 10:11:04 +00:00
list_display = ( ' abid ' , ' created_at ' , ' hostname ' , ' ips ' , ' os_platform ' , ' hw_in_docker ' , ' hw_in_vm ' , ' hw_manufacturer ' , ' hw_product ' , ' os_arch ' , ' os_family ' , ' os_release ' , ' hw_uuid ' , ' health ' )
2024-10-02 04:46:35 +00:00
sort_fields = ( ' abid ' , ' created_at ' , ' hostname ' , ' ips ' , ' os_platform ' , ' hw_in_docker ' , ' hw_in_vm ' , ' hw_manufacturer ' , ' hw_product ' , ' os_arch ' , ' os_family ' , ' os_release ' , ' hw_uuid ' )
# search_fields = ('id', 'abid', 'guid', 'hostname', 'hw_manufacturer', 'hw_product', 'hw_uuid', 'os_arch', 'os_family', 'os_platform', 'os_kernel', 'os_release')
readonly_fields = ( ' guid ' , ' created_at ' , ' modified_at ' , ' abid_info ' , ' ips ' )
2024-10-03 10:11:04 +00:00
fields = ( * readonly_fields , ' hostname ' , ' hw_in_docker ' , ' hw_in_vm ' , ' hw_manufacturer ' , ' hw_product ' , ' hw_uuid ' , ' os_arch ' , ' os_family ' , ' os_platform ' , ' os_kernel ' , ' os_release ' , ' stats ' , ' num_uses_succeeded ' , ' num_uses_failed ' )
2024-10-02 04:46:35 +00:00
list_filter = ( ' hw_in_docker ' , ' hw_in_vm ' , ' os_arch ' , ' os_family ' , ' os_platform ' )
ordering = [ ' -created_at ' ]
list_per_page = 100
2024-10-03 10:11:04 +00:00
actions = [ " delete_selected " ]
2024-10-02 04:46:35 +00:00
@admin.display (
description = ' Public IP ' ,
ordering = ' networkinterface__ip_public ' ,
)
def ips ( self , machine ) :
return format_html (
' <a href= " /admin/machine/networkinterface/?q= {} " ><b><code> {} </code></b></a> ' ,
machine . abid ,
' , ' . join ( machine . networkinterface_set . values_list ( ' ip_public ' , flat = True ) ) ,
)
@admin.register ( NetworkInterface , site = archivebox_admin )
class NetworkInterfaceAdmin ( ABIDModelAdmin ) :
2024-10-03 10:11:04 +00:00
list_display = ( ' abid ' , ' created_at ' , ' machine_info ' , ' ip_public ' , ' dns_server ' , ' isp ' , ' country ' , ' region ' , ' city ' , ' iface ' , ' ip_local ' , ' mac_address ' , ' health ' )
2024-10-02 04:46:35 +00:00
sort_fields = ( ' abid ' , ' created_at ' , ' machine_info ' , ' ip_public ' , ' dns_server ' , ' isp ' , ' country ' , ' region ' , ' city ' , ' iface ' , ' ip_local ' , ' mac_address ' )
search_fields = ( ' abid ' , ' machine__abid ' , ' iface ' , ' ip_public ' , ' ip_local ' , ' mac_address ' , ' dns_server ' , ' hostname ' , ' isp ' , ' city ' , ' region ' , ' country ' )
readonly_fields = ( ' machine ' , ' created_at ' , ' modified_at ' , ' abid_info ' , ' mac_address ' , ' ip_public ' , ' ip_local ' , ' dns_server ' )
2024-10-03 10:11:04 +00:00
fields = ( * readonly_fields , ' iface ' , ' hostname ' , ' isp ' , ' city ' , ' region ' , ' country ' , ' num_uses_succeeded ' , ' num_uses_failed ' )
2024-10-02 04:46:35 +00:00
list_filter = ( ' isp ' , ' country ' , ' region ' )
ordering = [ ' -created_at ' ]
list_per_page = 100
2024-10-03 10:11:04 +00:00
actions = [ " delete_selected " ]
2024-10-02 04:46:35 +00:00
@admin.display (
description = ' Machine ' ,
ordering = ' machine__abid ' ,
)
def machine_info ( self , iface ) :
return format_html (
' <a href= " /admin/machine/machine/ {} /change " ><b><code>[ {} ]</code></b> {} </a> ' ,
iface . machine . id ,
iface . machine . abid ,
iface . machine . hostname ,
)
2024-10-03 10:10:22 +00:00
@admin.register ( InstalledBinary , site = archivebox_admin )
class InstalledBinaryAdmin ( ABIDModelAdmin ) :
list_display = ( ' abid ' , ' created_at ' , ' machine_info ' , ' name ' , ' binprovider ' , ' version ' , ' abspath ' , ' sha256 ' , ' health ' )
sort_fields = ( ' abid ' , ' created_at ' , ' machine_info ' , ' name ' , ' binprovider ' , ' version ' , ' abspath ' , ' sha256 ' )
search_fields = ( ' abid ' , ' machine__abid ' , ' name ' , ' binprovider ' , ' version ' , ' abspath ' , ' sha256 ' )
readonly_fields = ( ' created_at ' , ' modified_at ' , ' abid_info ' )
fields = ( ' machine ' , ' name ' , ' binprovider ' , ' abspath ' , ' version ' , ' sha256 ' , * readonly_fields , ' num_uses_succeeded ' , ' num_uses_failed ' )
list_filter = ( ' name ' , ' binprovider ' , ' machine_id ' )
ordering = [ ' -created_at ' ]
list_per_page = 100
actions = [ " delete_selected " ]
@admin.display (
description = ' Machine ' ,
ordering = ' machine__abid ' ,
)
def machine_info ( self , installed_binary ) :
return format_html (
' <a href= " /admin/machine/machine/ {} /change " ><b><code>[ {} ]</code></b> {} </a> ' ,
installed_binary . machine . id ,
installed_binary . machine . abid ,
installed_binary . machine . hostname ,
)