Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions applications/luci-app-51ddns/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
include $(TOPDIR)/rules.mk

LUCI_TITLE:=LuCI support for 51DDNS remote access
LUCI_URL:=https://github.com/21hkcloud/51ddns-openwrt
LUCI_DESCRIPTION:=Configure and monitor the 51DDNS remote access agent.
LUCI_DEPENDS:=+luci-base +51ddns-agent
LUCI_PKGARCH:=all

PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Jinshuan Wang <21hkcloud@gmail.com>

include ../../luci.mk

# call BuildPackage - OpenWrt buildroot signature
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
'use strict';
'require view';
'require form';
'require rpc';
'require uci';

const callServiceList = rpc.declare({
object: 'service',
method: 'list',
params: [ 'name' ],
expect: { '': {} },
});

const callLocalStatus = rpc.declare({
object: 'luci.51ddns',
method: 'status',
expect: { '': {} },
});

const callAgentInfo = rpc.declare({
object: 'luci.51ddns',
method: 'info',
expect: { '': {} },
});

function serviceState(data) {
const instances = data?.['51ddns-agent']?.instances || {};
const running = Object.values(instances).some(instance => instance?.running);

return {
running,
label: running ? _('Running') : _('Not running'),
className: running ? 'alert-message success' : 'alert-message warning',
};
}

function formatDate(value) {
const date = new Date(value || '');

if (Number.isNaN(date.getTime()))
return _('Synchronizing');

return date.toLocaleString([], {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}

function remainingState(value) {
const expires = new Date(value || '');

if (Number.isNaN(expires.getTime()))
return { label: _('Synchronizing'), className: 'alert-message notice' };

const milliseconds = expires.getTime() - Date.now();
if (milliseconds <= 0)
return { label: _('Expired'), className: 'alert-message error' };

const hours = Math.ceil(milliseconds / 3600000);
const days = Math.ceil(hours / 24);
const label = hours > 48
? N_(days, 'About %d day', 'About %d days').format(days)
: N_(hours, '%d hour', '%d hours').format(hours);

return {
label,
className: milliseconds <= 3 * 86400000
? 'alert-message warning'
: 'alert-message success',
};
}

return view.extend({
load() {
return Promise.all([
uci.load('51ddns'),
L.resolveDefault(callServiceList('51ddns-agent'), {}),
L.resolveDefault(callLocalStatus(), {}),
L.resolveDefault(callAgentInfo(), {}),
]);
},

render(data) {
const state = serviceState(data[1]);
const local = data[2] || {};
const info = data[3] || {};
const plan = local.plan || null;
const remaining = remainingState(plan?.expires_at);
const map = new form.Map(
'51ddns',
_('51DDNS Remote Access'),
_('Enter the account token once to register this router automatically. Device identity and relay settings are managed by the 51DDNS control plane.'),
);
const section = map.section(form.NamedSection, 'main', 'agent', _('Quick setup'));
section.addremove = false;
Comment thread
21hkcloud marked this conversation as resolved.

const status = section.option(form.DummyValue, '_status', _('Service status'));
status.rawhtml = true;
status.cfgvalue = () =>
`<span class="${state.className}"><strong>${state.label}</strong></span>`;

const version = section.option(form.DummyValue, '_version', _('Agent version'));
version.cfgvalue = () => info.version || _('Unavailable');

const planName = section.option(form.DummyValue, '_plan_name', _('Current plan'));
planName.cfgvalue = () => plan?.product_name || _('No active plan');

const expiresAt = section.option(form.DummyValue, '_expires_at', _('Expires at'));
expiresAt.cfgvalue = () => plan ? formatDate(plan.expires_at) : _('Not bound');

const remainingTime = section.option(form.DummyValue, '_remaining_time', _('Time remaining'));
remainingTime.rawhtml = true;
remainingTime.cfgvalue = () => {
if (!plan)
return `<span class="alert-message warning"><strong>${_('Bind or purchase a plan')}</strong></span>`;

return `<span class="${remaining.className}"><strong>${remaining.label}</strong></span>`;
};

const consoleButton = section.option(form.Button, '_console', _('Console'));
consoleButton.inputtitle = _('Open 51DDNS Console');
consoleButton.inputstyle = 'apply';
consoleButton.onclick = () =>
window.open('https://console.51ddns.com/console#/workbench', '_blank', 'noopener,noreferrer');

const enabled = section.option(form.Flag, 'enabled', _('Enable'));
enabled.rmempty = false;
enabled.default = enabled.disabled;

const token = section.option(form.Value, 'account_token', _('Account token'));
token.password = true;
token.rmempty = false;
token.placeholder = '51d_...';
token.description = _('Copy the token from the 51DDNS console. All devices in the same account share this token.');

return map.render();
},
});
108 changes: 108 additions & 0 deletions applications/luci-app-51ddns/po/templates/51ddns.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:66
msgid "%d hour"
msgid_plural "%d hours"
msgstr[0] ""
msgstr[1] ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:94
#: applications/luci-app-51ddns/root/usr/share/luci/menu.d/luci-app-51ddns.json:3
msgid "51DDNS Remote Access"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:65
msgid "About %d day"
msgid_plural "About %d days"
msgstr[0] ""
msgstr[1] ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:133
msgid "Account token"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:105
msgid "Agent version"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:118
msgid "Bind or purchase a plan"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:123
msgid "Console"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:137
msgid ""
"Copy the token from the 51DDNS console. All devices in the same account "
"share this token."
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:108
msgid "Current plan"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:129
msgid "Enable"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:95
msgid ""
"Enter the account token once to register this router automatically. Device "
"identity and relay settings are managed by the 51DDNS control plane."
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:60
msgid "Expired"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:111
msgid "Expires at"
msgstr ""

#: applications/luci-app-51ddns/root/usr/share/rpcd/acl.d/luci-app-51ddns.json:3
msgid "Grant access to the 51DDNS configuration"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:109
msgid "No active plan"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:112
msgid "Not bound"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:32
msgid "Not running"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:124
msgid "Open 51DDNS Console"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:97
msgid "Quick setup"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:32
msgid "Running"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:100
msgid "Service status"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:41
#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:56
msgid "Synchronizing"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:114
msgid "Time remaining"
msgstr ""

#: applications/luci-app-51ddns/htdocs/luci-static/resources/view/51ddns/overview.js:106
msgid "Unavailable"
msgstr ""
59 changes: 59 additions & 0 deletions applications/luci-app-51ddns/root/usr/libexec/rpcd/luci.51ddns
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/sh

. /usr/share/libubox/jshn.sh
Comment thread
21hkcloud marked this conversation as resolved.

print_status() {
status_has_plan=0
status_product_name=''
status_expires_at=''
status_payload=''

if [ -s /var/lib/51ddns/status.json ]; then
status_payload="$(cat /var/lib/51ddns/status.json 2>/dev/null)"
json_load "$status_payload" 2>/dev/null

if json_select plan >/dev/null 2>&1; then
status_has_plan=1
json_get_var status_product_name product_name
json_get_var status_expires_at expires_at
fi
json_cleanup
fi

json_init
if [ "$status_has_plan" = 1 ]; then
json_add_object plan
json_add_string product_name "$status_product_name"
json_add_string expires_at "$status_expires_at"
json_close_object
fi
json_dump
json_cleanup
}

case "$1" in
list)
echo '{"status":{},"info":{}}'
;;
call)
case "$2" in
status)
print_status
;;
Comment thread
21hkcloud marked this conversation as resolved.
info)
version="$(/usr/bin/51ddns-agent --version 2>/dev/null | head -n 1)"
json_init
json_add_string version "$version"
json_dump
json_cleanup
;;
Comment thread
21hkcloud marked this conversation as resolved.
*)
echo '{"error":"method not found"}'
exit 1
;;
esac
;;
*)
exit 1
;;
esac
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"admin/services/51ddns": {
"title": "51DDNS Remote Access",
"order": 35,
"action": {
"type": "view",
"path": "51ddns/overview"
},
"depends": {
"acl": [ "luci-app-51ddns" ]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"luci-app-51ddns": {
"description": "Grant access to the 51DDNS configuration",
"read": {
"ubus": {
"luci.51ddns": [ "status", "info" ],
"service": [ "list" ]
},
"uci": [ "51ddns" ]
},
"write": {
"uci": [ "51ddns" ]
}
}
}