diff --git a/gulpfile.js b/gulpfile.js
index b0ac85809..2db35d717 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -50,7 +50,7 @@ if (argv.help || argv.h) {
process.exit(1);
}
-gulp.task('js', ['js-index', 'js-setup', 'js-setupdns', 'js-restore', 'js-update'], function () {});
+gulp.task('js', ['js-index', 'js-logs', 'js-setup', 'js-setupdns', 'js-restore', 'js-update'], function () {});
var oauth = {
clientId: argv.clientId || 'cid-webadmin',
@@ -90,6 +90,22 @@ gulp.task('js-index', function () {
.pipe(gulp.dest('webadmin/dist/js'));
});
+gulp.task('js-logs', function () {
+ // needs special treatment for error handling
+ var uglifyer = uglify();
+ uglifyer.on('error', function (error) {
+ console.error(error);
+ });
+
+ gulp.src(['webadmin/src/js/logs.js', 'webadmin/src/js/client.js'])
+ .pipe(ejs({ oauth: oauth }, {}, { ext: '.js' }))
+ .pipe(sourcemaps.init())
+ .pipe(concat('logs.js', { newLine: ';' }))
+ .pipe(uglifyer)
+ .pipe(sourcemaps.write())
+ .pipe(gulp.dest('webadmin/dist/js'));
+});
+
gulp.task('js-setup', function () {
// needs special treatment for error handling
var uglifyer = uglify();
@@ -209,6 +225,7 @@ gulp.task('watch', ['default'], function () {
gulp.watch(['webadmin/src/js/setup.js', 'webadmin/src/js/client.js'], ['js-setup']);
gulp.watch(['webadmin/src/js/setupdns.js', 'webadmin/src/js/client.js'], ['js-setupdns']);
gulp.watch(['webadmin/src/js/restore.js', 'webadmin/src/js/client.js'], ['js-restore']);
+ gulp.watch(['webadmin/src/js/logs.js', 'webadmin/src/js/client.js'], ['js-logs']);
gulp.watch(['webadmin/src/js/index.js', 'webadmin/src/js/client.js', 'webadmin/src/js/appstore.js', 'webadmin/src/js/main.js', 'webadmin/src/views/*.js'], ['js-index']);
gulp.watch(['webadmin/src/3rdparty/**/*'], ['3rdparty']);
});
diff --git a/webadmin/src/js/logs.js b/webadmin/src/js/logs.js
new file mode 100644
index 000000000..d0fc9b882
--- /dev/null
+++ b/webadmin/src/js/logs.js
@@ -0,0 +1,128 @@
+'use strict';
+
+/* global moment */
+
+// create main application module
+var app = angular.module('Application', ['angular-md5', 'ui-notification']);
+
+app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', function ($scope, $timeout, $location, Client) {
+ var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});
+
+ $scope.initialized = false;
+ $scope.installedApps = Client.getInstalledApps();
+ $scope.client = Client;
+ $scope.logs = [];
+ $scope.selected = '';
+ $scope.activeEventSource = null;
+ $scope.lines = 10;
+ $scope.selectedAppInfo = null;
+
+ // Add built-in log types for now
+ $scope.logs.push({ name: 'System (All)', type: 'platform', value: 'all', url: Client.makeURL('/api/v1/cloudron/logs?units=all') });
+ $scope.logs.push({ name: 'Box', type: 'platform', value: 'box', url: Client.makeURL('/api/v1/cloudron/logs?units=box') });
+ $scope.logs.push({ name: 'Mail', type: 'platform', value: 'mail', url: Client.makeURL('/api/v1/cloudron/logs?units=mail') });
+
+ $scope.error = function (error) {
+ console.error(error);
+ window.location.href = '/error.html';
+ };
+
+ function ab2str(buf) {
+ return String.fromCharCode.apply(null, new Uint16Array(buf));
+ }
+
+ $scope.clear = function () {
+ var logViewer = $('.logs-container');
+ logViewer.empty();
+ };
+
+ function showLogs() {
+ if (!$scope.selected) return;
+
+ var func = $scope.selected.type === 'platform' ? Client.getPlatformLogs : Client.getAppLogs;
+ func($scope.selected.value, true, $scope.lines, function handleLogs(error, result) {
+ if (error) return console.error(error);
+
+ $scope.activeEventSource = result;
+ result.onmessage = function handleMessage(message) {
+ var data;
+
+ try {
+ data = JSON.parse(message.data);
+ } catch (e) {
+ return console.error(e);
+ }
+
+ // check if we want to auto scroll (this is before the appending, as that skews the check)
+ var tmp = $('.logs-container');
+ var autoScroll = tmp[0].scrollTop > (tmp[0].scrollTopMax - 24);
+
+ var logLine = $('
');
+ var timeString = moment.utc(data.realtimeTimestamp/1000).format('MMM DD HH:mm:ss');
+ logLine.html('
' + timeString + ' ' + window.ansiToHTML(typeof data.message === 'string' ? data.message : ab2str(data.message)));
+ tmp.append(logLine);
+
+ if (autoScroll) tmp[0].lastChild.scrollIntoView({ behavior: 'instant', block: 'end' });
+ };
+ });
+ }
+
+ Client.onApps(function () {
+ if ($scope.selected.type !== 'app') return;
+
+ var appId = $scope.selected.value;
+
+ Client.getApp(appId, function (error, result) {
+ if (error) return console.error(error);
+
+ $scope.selectedAppInfo = result;
+ });
+ });
+
+ Client.getStatus(function (error, status) {
+ if (error) return $scope.error(error);
+
+ if (!status.activated) {
+ console.log('Not activated yet, redirecting', status);
+ window.location.href = '/';
+ return;
+ }
+
+ Client.refreshConfig(function (error) {
+ if (error) return $scope.error(error);
+
+ // check version and force reload if needed
+ if (!localStorage.version) {
+ localStorage.version = Client.getConfig().version;
+ } else if (localStorage.version !== Client.getConfig().version) {
+ localStorage.version = Client.getConfig().version;
+ window.location.reload(true);
+ }
+
+ Client.refreshInstalledApps(function (error) {
+ if (error) return $scope.error(error);
+
+ Client.getInstalledApps().forEach(function (app) {
+ $scope.logs.push({
+ type: 'app',
+ value: app.id,
+ name: app.fqdn + ' (' + app.manifest.title + ')',
+ url: Client.makeURL('/api/v1/apps/' + app.id + '/logs'),
+ addons: app.manifest.addons
+ });
+ });
+
+ // activate pre-selected log from query otherwise choose the first one
+ $scope.selected = $scope.logs.find(function (e) { return e.value === search.id; });
+ if (!$scope.selected) $scope.selected = $scope.logs[0];
+
+ // now mark the Client to be ready
+ Client.setReady();
+
+ $scope.initialized = true;
+
+ showLogs();
+ });
+ });
+ });
+}]);
diff --git a/webadmin/src/logs.html b/webadmin/src/logs.html
new file mode 100644
index 000000000..f4112cecd
--- /dev/null
+++ b/webadmin/src/logs.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
Cloudron Logs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ selected.name }} Logs
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webadmin/src/theme.scss b/webadmin/src/theme.scss
index 589076f61..d8b667123 100644
--- a/webadmin/src/theme.scss
+++ b/webadmin/src/theme.scss
@@ -1122,47 +1122,45 @@ footer {
// Logs
// ----------------------------
-.logs-controls {
- margin-top: 25px;
- margin-bottom: 10px;
+.logs {
+ background: black;
- .ng-isolate-scope {
- float: left;
- }
+ .logs-controls {
+ margin: 5px;
- h3 {
- display: inline-block;
- margin-top: 6px;
- margin-bottom: 0;
- }
-
- select {
- display: inline-block;
- width: 250px;
- margin-left: 20px;
- }
-}
-
-.logs-container {
- flex-grow: 1;
- margin-left: calc(8.33% + 15px);
- margin-right: calc(8.33% + 15px);
- margin-bottom: 20px;
- background-color: black;
- color: white;
- overflow: auto;
- padding: 5px;
- font-family: monospace;
-
- .log-line {
- line-height: 1.2;
-
- &:hover {
- background-color: #333333;
+ .ng-isolate-scope {
+ float: left;
}
- .time {
- color: #00FFFF;
+ h3 {
+ margin: 5px 0;
+ color: white;
+ }
+
+ select {
+ display: inline-block;
+ width: 250px;
+ }
+ }
+
+ .logs-container {
+ flex-grow: 1;
+ margin-bottom: 5px;
+ color: white;
+ overflow: auto;
+ padding: 5px;
+ font-family: monospace;
+
+ .log-line {
+ line-height: 1.2;
+
+ &:hover {
+ background-color: #333333;
+ }
+
+ .time {
+ color: #00FFFF;
+ }
}
}
}
diff --git a/webadmin/src/views/logs.html b/webadmin/src/views/logs.html
deleted file mode 100644
index be3393800..000000000
--- a/webadmin/src/views/logs.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/webadmin/src/views/logs.js b/webadmin/src/views/logs.js
deleted file mode 100644
index a2c0bd3fc..000000000
--- a/webadmin/src/views/logs.js
+++ /dev/null
@@ -1,119 +0,0 @@
-'use strict';
-
-/* global moment */
-
-angular.module('Application').controller('LogsController', ['$scope', '$location', '$route', '$routeParams', 'Client', function ($scope, $location, $route, $routeParams, Client) {
- Client.onReady(function () { if (!Client.getUserInfo().admin) $location.path('/'); });
-
- $scope.config = Client.getConfig();
- $scope.user = Client.getUserInfo();
-
- $scope.logs = [];
- $scope.selected = '';
- $scope.activeEventSource = null;
- $scope.lines = 10;
- $scope.selectedAppInfo = null;
-
- function ab2str(buf) {
- return String.fromCharCode.apply(null, new Uint16Array(buf));
- }
-
- $scope.populateLogTypes = function () {
- $scope.logs.push({ name: 'System (All)', type: 'platform', value: 'all', url: Client.makeURL('/api/v1/cloudron/logs?units=all') });
- $scope.logs.push({ name: 'Box', type: 'platform', value: 'box', url: Client.makeURL('/api/v1/cloudron/logs?units=box') });
- $scope.logs.push({ name: 'Mail', type: 'platform', value: 'mail', url: Client.makeURL('/api/v1/cloudron/logs?units=mail') });
-
- Client.getInstalledApps().forEach(function (app) {
- $scope.logs.push({
- type: 'app',
- value: app.id,
- name: app.fqdn + ' (' + app.manifest.title + ')',
- url: Client.makeURL('/api/v1/apps/' + app.id + '/logs'),
- addons: app.manifest.addons
- });
- });
-
- // activate pre-selected log from query
- $scope.selected = $scope.logs.find(function (e) { return e.value === $routeParams.id; });
-
- if (!$scope.selected) {
- $scope.selected = $scope.logs[0];
- }
- };
-
- function reset() {
- // close the old event source so we wont receive any new logs
- if ($scope.activeEventSource) {
- $scope.activeEventSource.close();
- $scope.activeEventSource = null;
- }
-
- var logViewer = $('.logs-container');
- logViewer.empty();
-
- $scope.selectedAppInfo = null;
- }
-
- $scope.showLogs = function () {
- reset();
-
- if (!$scope.selected) return;
-
- var func = $scope.selected.type === 'platform' ? Client.getPlatformLogs : Client.getAppLogs;
- func($scope.selected.value, true, $scope.lines, function handleLogs(error, result) {
- if (error) return console.error(error);
-
- $scope.activeEventSource = result;
- result.onmessage = function handleMessage(message) {
- var data;
-
- try {
- data = JSON.parse(message.data);
- } catch (e) {
- return console.error(e);
- }
-
- // check if we want to auto scroll (this is before the appending, as that skews the check)
- var tmp = $('.logs-container');
- var autoScroll = tmp[0].scrollTop > (tmp[0].scrollTopMax - 24);
-
- var logLine = $('
');
- var timeString = moment.utc(data.realtimeTimestamp/1000).format('MMM DD HH:mm:ss');
- logLine.html('' + timeString + ' ' + window.ansiToHTML(typeof data.message === 'string' ? data.message : ab2str(data.message)));
- tmp.append(logLine);
-
- if (autoScroll) tmp[0].lastChild.scrollIntoView({ behavior: 'instant', block: 'end' });
- };
- });
- };
-
- $scope.$watch('selected', function (newVal) {
- if (!newVal) return;
-
- $route.updateParams({ id: newVal.value });
- $scope.showLogs();
- });
-
- Client.onReady($scope.populateLogTypes);
-
- Client.onApps(function () {
- if ($scope.$$destroyed) return;
- if ($scope.selected.type !== 'app') return;
-
- var appId = $scope.selected.value;
-
- Client.getApp(appId, function (error, result) {
- if (error) return console.error(error);
-
- $scope.selectedAppInfo = result;
- });
- });
-
- $scope.$on('$destroy', function () {
- if ($scope.activeEventSource) {
- $scope.activeEventSource.onmessage = function () {};
- $scope.activeEventSource.close();
- $scope.activeEventSource = null;
- }
- });
-}]);