diff --git a/media/css/seahub.css b/media/css/seahub.css index c529a2c2b4..87f212b2ed 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -820,6 +820,16 @@ textarea:-moz-placeholder {/* for FF */ margin:0; color:#000; } +/***** header *****/ +.header-bar { + padding:9px 10px; + background:#f2f2f2; + margin-bottom:.5em; + border-radius:2px; + padding-bottom:0; + height:39px; + overflow:hidden; +} /**** narrow-panel ****/ .narrow-panel { width:320px; diff --git a/seahub/api2/endpoints/admin/sysinfo.py b/seahub/api2/endpoints/admin/sysinfo.py new file mode 100644 index 0000000000..1ad51837be --- /dev/null +++ b/seahub/api2/endpoints/admin/sysinfo.py @@ -0,0 +1,117 @@ +import logging +import os + +from django.utils.dateformat import DateFormat +from django.utils.translation import ugettext as _ +from django.template.defaultfilters import filesizeformat + +from rest_framework.authentication import SessionAuthentication +from rest_framework.permissions import IsAuthenticated, IsAdminUser +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status + +from seaserv import seafile_api, ccnet_api +from pysearpc import SearpcError + +from seahub.utils.rpc import mute_seafile_api +from seahub.utils import is_pro_version +from seahub.utils.licenseparse import parse_license + +from seahub.api2.utils import api_error +from seahub.api2.authentication import TokenAuthentication +from seahub.api2.throttling import UserRateThrottle + +import seahub.settings +try: + from seahub.settings import MULTI_TENANCY +except ImportError: + MULTI_TENANCY = False + +logger = logging.getLogger(__name__) + +class SysInfo(APIView): + """Show system info. + """ + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAdminUser,) + + def get(self, request, format=None): + # count repos + repos_count = mute_seafile_api.count_repos() + + # count groups + try: + groups_count = len(ccnet_api.get_all_groups(-1, -1)) + except Exception as e: + logger.error(e) + groups_count = 0 + + # count orgs + if MULTI_TENANCY: + multi_tenacy_enabled = True + try: + org_count = ccnet_api.count_orgs() + except Exception as e: + logger.error(e) + org_count = 0 + else: + multi_tenacy_enabled = False + org_count = 0 + + # count users + try: + active_db_users = ccnet_api.count_emailusers('DB') + except Exception as e: + logger.error(e) + active_db_users = 0 + + try: + active_ldap_users = ccnet_api.count_emailusers('LDAP') + except Exception as e: + logger.error(e) + active_ldap_users = 0 + + try: + inactive_db_users = ccnet_api.count_inactive_emailusers('DB') + except Exception as e: + logger.error(e) + inactive_db_users = 0 + + try: + inactive_ldap_users = ccnet_api.count_inactive_emailusers('LDAP') + except Exception as e: + logger.error(e) + inactive_ldap_users = 0 + + active_users = active_db_users + active_ldap_users if active_ldap_users > 0 \ + else active_db_users + + inactive_users = inactive_db_users + inactive_ldap_users if inactive_ldap_users > 0 \ + else inactive_db_users + + is_pro = is_pro_version() + if is_pro: + license_file = os.path.join(seahub.settings.PROJECT_ROOT, '../../seafile-license.txt') + license_dict = parse_license(license_file) + else: + license_dict = {} + + if license_dict: + with_license = True + else: + with_license = False + + info = { + 'users_count': active_users + inactive_users, + 'active_users_count': active_users, + 'repos_count': repos_count, + 'groups_count': groups_count, + 'org_count': org_count, + 'multi_tenancy_enabled': multi_tenacy_enabled, + 'is_pro': is_pro, + 'with_license': with_license, + 'license': license_dict + } + + return Response(info) diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html index 730dcdaa50..48c9754ef3 100644 --- a/seahub/templates/js/sysadmin-templates.html +++ b/seahub/templates/js/sysadmin-templates.html @@ -46,3 +46,56 @@ {% endif %} + + diff --git a/seahub/urls.py b/seahub/urls.py index 9b5371a472..626c0e5530 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -28,6 +28,7 @@ from seahub.api2.endpoints.admin.login import Login from seahub.api2.endpoints.admin.file_audit import FileAudit from seahub.api2.endpoints.admin.file_update import FileUpdate from seahub.api2.endpoints.admin.perm_audit import PermAudit +from seahub.api2.endpoints.admin.sysinfo import SysInfo # Uncomment the next two lines to enable the admin: #from django.contrib import admin @@ -189,6 +190,7 @@ urlpatterns = patterns( url(r'^api/v2.1/repos/(?P[-0-9-a-f]{36})/file/$', FileView.as_view(), name='api-v2.1-file-view'), url(r'^api/v2.1/repos/(?P[-0-9-a-f]{36})/dir/$', DirView.as_view(), name='api-v2.1-dir-view'), url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/set-password/$', RepoSetPassword.as_view(), name="api-v2.1-repo-set-password"), + url(r'^api/v2.1/admin/sysinfo/$', SysInfo.as_view(), name='api-v2.1-sysinfo'), (r'^avatar/', include('seahub.avatar.urls')), (r'^notification/', include('seahub.notifications.urls')), diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py index 2ac735a4f0..c24652cba3 100644 --- a/seahub/utils/__init__.py +++ b/seahub/utils/__init__.py @@ -1026,11 +1026,11 @@ if HAS_OFFICE_CONVERTER: def cluster_delegate(delegate_func): '''usage: - + @cluster_delegate(funcA) def func(*args): ...non-cluster logic goes here... - + - In non-cluster mode, this decorator effectively does nothing. - In cluster mode, if this node is not the office convert node, funcA is called instead of the decorated function itself @@ -1261,6 +1261,10 @@ def do_urlopen(url, data=None, headers=None): return ret def is_pro_version(): + if seahub.settings.DEBUG: + if hasattr(seahub.settings, 'IS_PRO_VERSION') \ + and seahub.settings.IS_PRO_VERSION: + return True if EVENTS_CONFIG_FILE: return True else: diff --git a/static/scripts/common.js b/static/scripts/common.js index 0093406058..ec94c12687 100644 --- a/static/scripts/common.js +++ b/static/scripts/common.js @@ -154,6 +154,8 @@ define([ case 'search_user': return siteRoot + 'api2/search-user/'; case 'user_profile': return siteRoot + 'profile/' + options.username + '/'; case 'space_and_traffic': return siteRoot + 'ajax/space_and_traffic/'; + // sysadmin + case 'sysinfo': return siteRoot + 'api/v2.1/admin/sysinfo/'; } }, diff --git a/static/scripts/sysadmin-app/models/sysinfo.js b/static/scripts/sysadmin-app/models/sysinfo.js new file mode 100644 index 0000000000..272dd89dbe --- /dev/null +++ b/static/scripts/sysadmin-app/models/sysinfo.js @@ -0,0 +1,17 @@ +define([ + 'underscore', + 'backbone', + 'common' +], function(_, Backbone, Common) { + 'use strict'; + + var SysInfo = Backbone.Model.extend({ + + url: function () { + return Common.getUrl({name: 'sysinfo'}); + }, + + }); + + return SysInfo; +}); diff --git a/static/scripts/sysadmin-app/router.js b/static/scripts/sysadmin-app/router.js index 5046cdea71..556f6a15ca 100644 --- a/static/scripts/sysadmin-app/router.js +++ b/static/scripts/sysadmin-app/router.js @@ -41,6 +41,7 @@ define([ showDashboard: function() { this.switchCurrentView(this.dashboardView); this.sideNavView.setCurTab('dashboard'); + this.dashboardView.show(); } }); diff --git a/static/scripts/sysadmin-app/views/dashboard.js b/static/scripts/sysadmin-app/views/dashboard.js index c66d8864a0..fb9e67ae74 100644 --- a/static/scripts/sysadmin-app/views/dashboard.js +++ b/static/scripts/sysadmin-app/views/dashboard.js @@ -2,23 +2,43 @@ define([ 'jquery', 'underscore', 'backbone', - 'common' -], function($, _, Backbone, Common) { + 'common', + 'sysadmin-app/models/SysInfo' +], function($, _, Backbone, Common, SysInfo) { 'use strict'; var DashboardView = Backbone.View.extend({ tagName: 'div', + template: _.template($("#sysinfo-tmpl").html()), initialize: function() { - + this.sysinfo = new SysInfo(); }, events: { }, render: function() { - + var _this = this; + this.sysinfo.fetch({ + success: function(model, response, options) { + _this._showContent(); + }, + error: function(model, response, options) { + var err_msg; + if (response.responseText) { + if (response['status'] == 401 || response['status'] == 403) { + err_msg = gettext("Permission error"); + } else { + err_msg = $.parseJSON(response.responseText).error_msg; + } + } else { + err_msg = gettext("Failed. Please check the network."); + } + Common.feedback(err_msg, 'error'); + } + }); }, hide: function() { @@ -26,7 +46,15 @@ define([ }, show: function() { + this.render(); + }, + + _showContent: function() { + console.log(this.sysinfo.toJSON()); + this.$el.html(this.template(this.sysinfo.toJSON())); + $("#right-panel").html(this.$el); + return this; } });