diff --git a/swh/web/admin/deposit.py b/swh/web/admin/deposit.py
new file mode 100644
index 00000000..1f88f3a0
--- /dev/null
+++ b/swh/web/admin/deposit.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2018 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+import json
+import requests
+
+from django.core.cache import cache
+from django.conf import settings
+from django.contrib.admin.views.decorators import staff_member_required
+from django.core.paginator import Paginator
+from django.http import HttpResponse
+from django.shortcuts import render
+
+from requests.auth import HTTPBasicAuth
+
+from swh.web.admin.adminurls import admin_route
+from swh.web.config import get_config
+
+config = get_config()['deposit']
+
+
+@admin_route(r'deposit/', view_name='admin-deposit')
+@staff_member_required(login_url=settings.LOGIN_URL)
+def _admin_origin_save(request):
+ return render(request, 'admin/deposit.html')
+
+
+@admin_route(r'deposit/list/',
+ view_name='admin-deposit-list')
+@staff_member_required(login_url=settings.LOGIN_URL)
+def _admin_deposit_list(request):
+ table_data = {}
+ table_data['draw'] = int(request.GET['draw'])
+ deposits_list_url = config['private_api_url'] + 'deposits'
+ deposits_list_auth = HTTPBasicAuth(config['private_api_user'],
+ config['private_api_password'])
+
+ try:
+ nb_deposits = requests.get('%s?page_size=1' % deposits_list_url,
+ auth=deposits_list_auth).json()['count']
+
+ deposits_data = cache.get('swh-deposit-list')
+ if not deposits_data or deposits_data['count'] != nb_deposits:
+ deposits_data = requests.get('%s?page_size=%s' %
+ (deposits_list_url, nb_deposits),
+ auth=deposits_list_auth).json()
+ cache.set('swh-deposit-list', deposits_data)
+
+ deposits = deposits_data['results']
+
+ search_value = request.GET['search[value]']
+ if search_value:
+ deposits = \
+ [d for d in deposits
+ if any(search_value.lower() in val
+ for val in [str(v).lower() for v in d.values()])]
+
+ column_order = request.GET['order[0][column]']
+ field_order = request.GET['columns[%s][name]' % column_order]
+ order_dir = request.GET['order[0][dir]']
+
+ deposits = sorted(deposits, key=lambda d: d[field_order] or '')
+ if order_dir == 'desc':
+ deposits = list(reversed(deposits))
+
+ length = int(request.GET['length'])
+ page = int(request.GET['start']) / length + 1
+ paginator = Paginator(deposits, length)
+ data = paginator.page(page).object_list
+ table_data['recordsTotal'] = deposits_data['count']
+ table_data['recordsFiltered'] = len(deposits)
+ table_data['data'] = [{'id': d['id'],
+ 'external_id': d['external_id'],
+ 'reception_date': d['reception_date'],
+ 'status': d['status'],
+ 'status_detail': d['status_detail'],
+ 'swh_id': d['swh_id']
+ } for d in data]
+ except Exception:
+ table_data['error'] = 'An error occurred while retrieving the list of deposits !' # noqa
+
+ return HttpResponse(json.dumps(table_data),
+ content_type='application/json')
diff --git a/swh/web/admin/urls.py b/swh/web/admin/urls.py
index f68613a5..1f5a2d7a 100644
--- a/swh/web/admin/urls.py
+++ b/swh/web/admin/urls.py
@@ -1,25 +1,26 @@
# Copyright (C) 2018 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from django.shortcuts import redirect
from swh.web.admin.adminurls import AdminUrls
import swh.web.admin.origin_save # noqa
+import swh.web.admin.deposit # noqa
def admin_default_view(request):
return redirect('admin-origin-save')
urlpatterns = [url(r'^$', admin_default_view, name='admin'),
url(r'^login/$', auth_views.login,
{'template_name': 'login.html'}, name='login'),
url(r'^logout/$', auth_views.logout,
{'template_name': 'logout.html'}, name='logout')]
urlpatterns += AdminUrls.get_url_patterns()
diff --git a/swh/web/assets/src/bundles/admin/deposit.js b/swh/web/assets/src/bundles/admin/deposit.js
new file mode 100644
index 00000000..a899964e
--- /dev/null
+++ b/swh/web/assets/src/bundles/admin/deposit.js
@@ -0,0 +1,79 @@
+export function initDepositAdmin() {
+ let depositsTable;
+ $(document).ready(() => {
+ $.fn.dataTable.ext.errMode = 'none';
+ depositsTable = $('#swh-admin-deposit-list')
+ .on('error.dt', (e, settings, techNote, message) => {
+ $('#swh-admin-deposit-list-error').text(message);
+ })
+ .DataTable({
+ serverSide: true,
+ ajax: Urls.admin_deposit_list(),
+ columns: [
+ {
+ data: 'id',
+ name: 'id'
+ },
+ {
+ data: 'external_id',
+ name: 'external_id',
+ render: (data, type, row) => {
+ if (type === 'display') {
+ if (data && data.startsWith('hal')) {
+ return `${data}`;
+ }
+ }
+ return data;
+ }
+ },
+ {
+ data: 'reception_date',
+ name: 'reception_date',
+ render: (data, type, row) => {
+ if (type === 'display') {
+ let date = new Date(data);
+ return date.toLocaleString();
+ }
+ return data;
+ }
+ },
+ {
+ data: 'status',
+ name: 'status'
+ },
+ {
+ data: 'status_detail',
+ name: 'status_detail',
+ render: (data, type, row) => {
+ if (type === 'display' && data) {
+ let text = data;
+ if (typeof data === 'object') {
+ text = JSON.stringify(data, null, 4);
+ }
+ return `
${text}
`;
+ }
+ return data;
+ },
+ orderable: false
+ },
+ {
+ data: 'swh_id',
+ name: 'swh_id',
+ render: (data, type, row) => {
+ if (type === 'display') {
+ if (data && data.startsWith('swh')) {
+ let browseUrl = Urls.browse_swh_id(data);
+ return `${data}`;
+ }
+ }
+ return data;
+ }
+ }
+ ],
+ scrollY: '50vh',
+ scrollCollapse: true,
+ order: [[0, 'desc']]
+ });
+ depositsTable.draw();
+ });
+}
diff --git a/swh/web/assets/src/bundles/admin/index.js b/swh/web/assets/src/bundles/admin/index.js
index c2ab2bbf..6910bd3a 100644
--- a/swh/web/assets/src/bundles/admin/index.js
+++ b/swh/web/assets/src/bundles/admin/index.js
@@ -1,8 +1,9 @@
/**
* Copyright (C) 2018 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
*/
+export * from './deposit';
export * from './origin-save';
diff --git a/swh/web/config.py b/swh/web/config.py
index 596a60ee..e4052284 100644
--- a/swh/web/config.py
+++ b/swh/web/config.py
@@ -1,123 +1,128 @@
# Copyright (C) 2017-2018 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information
from swh.core import config
from swh.storage import get_storage
from swh.indexer.storage import get_indexer_storage
from swh.vault.api.client import RemoteVaultClient
from swh.scheduler import get_scheduler
DEFAULT_CONFIG = {
'allowed_hosts': ('list', []),
'storage': ('dict', {
'cls': 'remote',
'args': {
'url': 'http://127.0.0.1:5002/',
'timeout': 10,
},
}),
'indexer_storage': ('dict', {
'cls': 'remote',
'args': {
'url': 'http://127.0.0.1:5007/',
'timeout': 1,
}
}),
'vault': ('string', 'http://127.0.0.1:5005/'),
'log_dir': ('string', '/tmp/swh/log'),
'debug': ('bool', False),
'host': ('string', '127.0.0.1'),
'port': ('int', 5004),
'secret_key': ('string', 'development key'),
# do not display code highlighting for content > 1MB
'content_display_max_size': ('int', 1024 * 1024),
'throttling': ('dict', {
'cache_uri': None, # production: memcached as cache (127.0.0.1:11211)
# development: in-memory cache so None
'scopes': {
'swh_api': {
'limiter_rate': {
'default': '120/h'
},
'exempted_networks': ['127.0.0.0/8']
},
'swh_vault_cooking': {
'limiter_rate': {
'default': '120/h',
'GET': '60/m'
},
'exempted_networks': ['127.0.0.0/8']
},
'swh_save_origin': {
'limiter_rate': {
'default': '120/h',
'POST': '10/h'
},
'exempted_networks': ['127.0.0.0/8']
}
}
}),
'scheduler': ('dict', {
'cls': 'remote',
'args': {
'url': 'http://localhost:5008/'
}
}),
'grecaptcha': ('dict', {
'validation_url': 'https://www.google.com/recaptcha/api/siteverify',
'site_key': '',
'private_key': ''
}),
- 'production_db': ('string', '/var/lib/swh/web.sqlite3')
+ 'production_db': ('string', '/var/lib/swh/web.sqlite3'),
+ 'deposit': ('dict', {
+ 'private_api_url': 'https://deposit.softwareheritage.org/1/private/',
+ 'private_api_user': 'swhworker',
+ 'private_api_password': ''
+ })
}
swhweb_config = {}
def get_config(config_file='webapp/webapp'):
"""Read the configuration file `config_file`, update the app with
parameters (secret_key, conf) and return the parsed configuration as a
dict. If no configuration file is provided, return a default
configuration."""
if not swhweb_config:
cfg = config.load_named_config(config_file, DEFAULT_CONFIG)
swhweb_config.update(cfg)
config.prepare_folders(swhweb_config, 'log_dir')
swhweb_config['storage'] = get_storage(**swhweb_config['storage'])
swhweb_config['vault'] = RemoteVaultClient(swhweb_config['vault'])
swhweb_config['indexer_storage'] = \
get_indexer_storage(**swhweb_config['indexer_storage'])
swhweb_config['scheduler'] = get_scheduler(**swhweb_config['scheduler']) # noqa
return swhweb_config
def storage():
"""Return the current application's SWH storage.
"""
return get_config()['storage']
def vault():
"""Return the current application's SWH vault.
"""
return get_config()['vault']
def indexer_storage():
"""Return the current application's SWH indexer storage.
"""
return get_config()['indexer_storage']
def scheduler():
"""Return the current application's SWH scheduler.
"""
return get_config()['scheduler']
diff --git a/swh/web/templates/admin/deposit.html b/swh/web/templates/admin/deposit.html
new file mode 100644
index 00000000..f65f53ad
--- /dev/null
+++ b/swh/web/templates/admin/deposit.html
@@ -0,0 +1,45 @@
+{% extends "layout.html" %}
+
+{% comment %}
+Copyright (C) 2018 The Software Heritage developers
+See the AUTHORS file at the top-level directory of this distribution
+License: GNU Affero General Public License version 3, or any later version
+See top-level LICENSE file for more information
+{% endcomment %}
+
+{% load swh_templatetags %}
+{% load render_bundle from webpack_loader %}
+
+{% block header %}
+{{ block.super }}
+{% render_bundle 'admin' %}
+{% endblock %}
+
+{% block title %} Deposit administration {% endblock %}
+
+{% block navbar-content %}
+
Deposit administration
+{% endblock %}
+
+{% block content %}
+
+ The table below displays the whole list of software deposits into the archive
+ submitted through HAL.
+
+
+
+
+
deposit id
+
external id
+
reception date
+
status
+
status detail
+
swh id
+
+
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/swh/web/templates/layout.html b/swh/web/templates/layout.html
index 981f3d79..80686e63 100644
--- a/swh/web/templates/layout.html
+++ b/swh/web/templates/layout.html
@@ -1,175 +1,181 @@
{% comment %}
Copyright (C) 2015-2018 The Software Heritage developers
See the AUTHORS file at the top-level directory of this distribution
License: GNU Affero General Public License version 3, or any later version
See top-level LICENSE file for more information
{% endcomment %}
{% load static %}
{% load render_bundle from webpack_loader %}
{% block title %}{% endblock %}
{% render_bundle 'vendors' %}
{% render_bundle 'webapp' %}
{% block header %}{% endblock %}