secubox-openwrt/package/secubox/luci-app-gotosocial/htdocs/luci-static/resources/view/gotosocial/users.js
CyberMind-FR f20bb1df6b feat(gotosocial): Add GoToSocial Fediverse server packages
Add secubox-app-gotosocial and luci-app-gotosocial for running a lightweight
ActivityPub social network server in LXC container.

Features:
- gotosocialctl CLI with install, start, stop, user management
- LXC container deployment (ARM64)
- HAProxy integration via emancipate command
- UCI configuration for instance, container, proxy, federation settings
- LuCI web interface with overview, users, and settings tabs
- Mesh integration support for auto-federation between SecuBox nodes
- Backup/restore functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 13:52:58 +01:00

259 lines
7.5 KiB
JavaScript

'use strict';
'require view';
'require rpc';
'require ui';
'require poll';
var callUsers = rpc.declare({
object: 'luci.gotosocial',
method: 'users',
expect: {}
});
var callCreateUser = rpc.declare({
object: 'luci.gotosocial',
method: 'create_user',
params: ['username', 'email', 'password', 'admin'],
expect: {}
});
var callDeleteUser = rpc.declare({
object: 'luci.gotosocial',
method: 'delete_user',
params: ['username'],
expect: {}
});
var callPromoteUser = rpc.declare({
object: 'luci.gotosocial',
method: 'promote_user',
params: ['username'],
expect: {}
});
var callDemoteUser = rpc.declare({
object: 'luci.gotosocial',
method: 'demote_user',
params: ['username'],
expect: {}
});
return view.extend({
users: [],
load: function() {
return callUsers();
},
pollUsers: function() {
return callUsers().then(L.bind(function(res) {
this.users = res.users || [];
this.updateUserTable();
}, this));
},
updateUserTable: function() {
var tbody = document.getElementById('users-tbody');
if (!tbody) return;
while (tbody.firstChild) {
tbody.removeChild(tbody.firstChild);
}
if (this.users.length === 0) {
tbody.appendChild(E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td', 'colspan': '5', 'style': 'text-align:center' }, _('No users found'))
]));
return;
}
this.users.forEach(L.bind(function(user) {
var row = E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td' }, user.username || '-'),
E('td', { 'class': 'td' }, user.email || '-'),
E('td', { 'class': 'td' }, user.admin ?
E('span', { 'class': 'badge success' }, _('Admin')) :
E('span', { 'class': 'badge' }, _('User'))
),
E('td', { 'class': 'td' }, user.confirmed ?
E('span', { 'class': 'badge success' }, _('Confirmed')) :
E('span', { 'class': 'badge warning' }, _('Pending'))
),
E('td', { 'class': 'td' }, [
user.admin ?
E('button', {
'class': 'btn cbi-button-neutral',
'click': ui.createHandlerFn(this, this.handleDemote, user.username)
}, _('Demote')) :
E('button', {
'class': 'btn cbi-button-action',
'click': ui.createHandlerFn(this, this.handlePromote, user.username)
}, _('Promote')),
' ',
E('button', {
'class': 'btn cbi-button-negative',
'click': ui.createHandlerFn(this, this.handleDelete, user.username)
}, _('Delete'))
])
]);
tbody.appendChild(row);
}, this));
},
handleCreate: function() {
return ui.showModal(_('Create User'), [
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Username')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'text', 'id': 'new-username', 'class': 'cbi-input-text', 'placeholder': 'johndoe' })
])
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Email')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'email', 'id': 'new-email', 'class': 'cbi-input-text', 'placeholder': 'john@example.com' })
])
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Password')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'password', 'id': 'new-password', 'class': 'cbi-input-text' })
])
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Admin')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'checkbox', 'id': 'new-admin' }),
' ', _('Grant admin privileges')
])
]),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'btn', 'click': ui.hideModal }, _('Cancel')),
' ',
E('button', {
'class': 'btn cbi-button-action',
'click': ui.createHandlerFn(this, function() {
var username = document.getElementById('new-username').value;
var email = document.getElementById('new-email').value;
var password = document.getElementById('new-password').value;
var admin = document.getElementById('new-admin').checked;
if (!username || !email || !password) {
ui.addNotification(null, E('p', _('All fields are required')), 'error');
return;
}
ui.hideModal();
ui.showModal(_('Creating User...'), [
E('p', { 'class': 'spinning' }, _('Creating user...'))
]);
return callCreateUser(username, email, password, admin).then(L.bind(function(res) {
ui.hideModal();
if (res.success) {
ui.addNotification(null, E('p', res.message || _('User created successfully')), 'success');
return this.pollUsers();
} else {
ui.addNotification(null, E('p', res.error || _('Failed to create user')), 'error');
}
}, this));
})
}, _('Create'))
])
]);
},
handleDelete: function(username) {
return ui.showModal(_('Delete User'), [
E('p', _('Are you sure you want to delete user "%s"?').format(username)),
E('p', { 'class': 'alert-message warning' }, _('This action cannot be undone. All posts and data will be lost.')),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'btn', 'click': ui.hideModal }, _('Cancel')),
' ',
E('button', {
'class': 'btn cbi-button-negative',
'click': ui.createHandlerFn(this, function() {
ui.hideModal();
return callDeleteUser(username).then(L.bind(function(res) {
if (res.success) {
ui.addNotification(null, E('p', res.message || _('User deleted')), 'success');
return this.pollUsers();
} else {
ui.addNotification(null, E('p', res.error || _('Failed to delete user')), 'error');
}
}, this));
})
}, _('Delete'))
])
]);
},
handlePromote: function(username) {
return callPromoteUser(username).then(L.bind(function(res) {
if (res.success) {
ui.addNotification(null, E('p', res.message || _('User promoted')), 'success');
return this.pollUsers();
} else {
ui.addNotification(null, E('p', res.error || _('Failed to promote user')), 'error');
}
}, this));
},
handleDemote: function(username) {
return callDemoteUser(username).then(L.bind(function(res) {
if (res.success) {
ui.addNotification(null, E('p', res.message || _('User demoted')), 'success');
return this.pollUsers();
} else {
ui.addNotification(null, E('p', res.error || _('Failed to demote user')), 'error');
}
}, this));
},
render: function(data) {
this.users = data.users || [];
var view = E('div', { 'class': 'cbi-map' }, [
E('h2', _('GoToSocial Users')),
E('div', { 'class': 'cbi-map-descr' }, _('Manage user accounts for your Fediverse instance.')),
E('div', { 'class': 'cbi-section' }, [
E('div', { 'style': 'margin-bottom:10px' }, [
E('button', {
'class': 'btn cbi-button-action',
'click': ui.createHandlerFn(this, this.handleCreate)
}, _('Create User'))
]),
E('table', { 'class': 'table' }, [
E('thead', {}, [
E('tr', { 'class': 'tr' }, [
E('th', { 'class': 'th' }, _('Username')),
E('th', { 'class': 'th' }, _('Email')),
E('th', { 'class': 'th' }, _('Role')),
E('th', { 'class': 'th' }, _('Status')),
E('th', { 'class': 'th' }, _('Actions'))
])
]),
E('tbody', { 'id': 'users-tbody' })
])
]),
E('style', {}, `
.badge { padding: 2px 8px; border-radius: 3px; background: #666; color: white; }
.badge.success { background: #4CAF50; }
.badge.warning { background: #FF9800; }
`)
]);
this.updateUserTable();
poll.add(L.bind(this.pollUsers, this), 10);
return view;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});