├── PDNS_Manager_HL_Overview.png
├── PDNS_Manager_login_window.png
├── PDNS_Manager_records_window.png
├── PDNS_Manager_signup_window.png
├── res
├── css
│ ├── webfonts
│ │ ├── fa-brands-400.eot
│ │ ├── fa-brands-400.ttf
│ │ ├── fa-solid-900.eot
│ │ ├── fa-solid-900.ttf
│ │ ├── fa-solid-900.woff
│ │ ├── fa-brands-400.woff
│ │ ├── fa-brands-400.woff2
│ │ ├── fa-regular-400.eot
│ │ ├── fa-regular-400.ttf
│ │ ├── fa-regular-400.woff
│ │ ├── fa-regular-400.woff2
│ │ └── fa-solid-900.woff2
│ ├── toastr.min.css
│ └── awesome
│ │ └── all.min.css
└── js
│ ├── toastr.min.js
│ ├── api.js
│ └── toastr.js.map
├── api2
├── credentials-sample.php
├── settings-sample.php
├── auth_helper.php
├── index.php
└── pdns_helper.php
├── composer.json
├── LICENSE
├── README.md
└── index.html
/PDNS_Manager_HL_Overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/PDNS_Manager_HL_Overview.png
--------------------------------------------------------------------------------
/PDNS_Manager_login_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/PDNS_Manager_login_window.png
--------------------------------------------------------------------------------
/PDNS_Manager_records_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/PDNS_Manager_records_window.png
--------------------------------------------------------------------------------
/PDNS_Manager_signup_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/PDNS_Manager_signup_window.png
--------------------------------------------------------------------------------
/res/css/webfonts/fa-brands-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-brands-400.eot
--------------------------------------------------------------------------------
/res/css/webfonts/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-brands-400.ttf
--------------------------------------------------------------------------------
/res/css/webfonts/fa-solid-900.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-solid-900.eot
--------------------------------------------------------------------------------
/res/css/webfonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/res/css/webfonts/fa-solid-900.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-solid-900.woff
--------------------------------------------------------------------------------
/res/css/webfonts/fa-brands-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-brands-400.woff
--------------------------------------------------------------------------------
/res/css/webfonts/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-brands-400.woff2
--------------------------------------------------------------------------------
/res/css/webfonts/fa-regular-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-regular-400.eot
--------------------------------------------------------------------------------
/res/css/webfonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/res/css/webfonts/fa-regular-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-regular-400.woff
--------------------------------------------------------------------------------
/res/css/webfonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/res/css/webfonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbeskrovny/PDNS-Manager/HEAD/res/css/webfonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/api2/credentials-sample.php:
--------------------------------------------------------------------------------
1 | array('password' => 'some_md5_password_hash', 'secret' => 'some_otp_secret'),
7 |
8 |
9 |
10 | ]);
11 |
12 |
13 |
14 | define('DDNS_TOKENS',
15 | [
16 | 'some_token' => 'some_token_description',
17 |
18 |
19 | ]);
20 |
21 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "guzzlehttp/guzzle": "^7.0",
4 | "slim/slim": "4.*",
5 | "slim/psr7": "^1.3",
6 | "nyholm/psr7": "^1.3",
7 | "nyholm/psr7-server": "^1.0",
8 | "guzzlehttp/psr7": "^1.7",
9 | "http-interop/http-factory-guzzle": "^1.0",
10 | "laminas/laminas-diactoros": "^2.4",
11 | "defuse/php-encryption": "^2.2",
12 | "spomky-labs/otphp": "^8.3",
13 | "endroid/qr-code": "^3.9"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ben
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/api2/settings-sample.php:
--------------------------------------------------------------------------------
1 | 3x AUTH_INTERVAL (in seconds)
23 | define('AUTH_LIFE', 900);
24 |
25 |
26 | ## Replace with your own by running: php -f ./auth_helper.php
27 | define('AUTH_KEY', 'def0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000936a8ed840e6b8cdcf2c41e89331dabf7b1a13b51bcc93133f5ff5d31a36fe5146720e4390c9e98376e1ade741dc7dd8834d3cb6ea98b7bd8a29c6ef315ea42f');
28 |
29 |
30 | ## PDNS Api key
31 | define('API_KEY', '00000000000000000000000000000000');
32 |
33 |
34 | define('ZONE_DEFAULTS', [
35 | 'dns' => 'dns1.mydns.com.',
36 | 'hostmaster' => 'hostmaster.mydns.com.',
37 | 'nameservers' => ['dns1.mydns.com.', 'dns2.mydns.com.'],
38 | 'masters' => ['dns1.mydns.com.'],
39 | 'refresh' => 14400,
40 | 'retry' => 3600,
41 | 'expire' => 604800,
42 | 'ttl' => 3600,
43 | ]);
44 |
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PDNS-Manager
2 | Lightweight PowerDNS management frontend
3 |
4 |
5 | ### Intention and prerequisites
6 | 1. During my IT management responsibilities I found some time to contribute to our team of engineers and company overall.
7 | 2. Tired of looking for a decent PowerDNS frontend manager -> let's create our own.
8 | 3. Should be lightweight, dead simple and could be easily modified if needed.
9 | 4. No need for the 3rd party storage databases (MySQL, PostgreSQL, e.t.c. databases), complex frameworks -> direct communication with the PowerDNS using API
10 | 5. Use PHP, HTML and JavaScript (jQuery) only
11 | 6. Some extra security on top (OTP)
12 |
13 | - - - -
14 |
15 |
16 | ### What works
17 | 1. Adding and removing zones
18 | 2. Modifying records
19 | 3. Adding records (A, TXT, CNAME, MX, SRV)
20 | 4. DDNS minimal usage (check Wiki as well):
21 | * Add: https://pdns.yourdns.com/pdns/ddns/token=aabbcc/name=myhost.example.com[/content=1.2.3.4[/keep=1|0]
22 | * Remove: https://pdns.yourdns.com/pdns/ddns/token=aabbcc/name=myhost.example.com/content=null
23 |
24 | - - - -
25 |
26 |
27 | ### Requirements
28 | 1. PowerDNS
29 | 2. Web server
30 | 3. PHP
31 |
32 | - - - -
33 |
34 | ### Tested on
35 | 1. PHP-FPM: 7.2.4
36 | 2. Ubuntu: 18.04.5
37 | 3. NGINX: 1.14.0
38 |
39 | - - - -
40 |
41 | ### Disclaimer
42 | 1. Use at your own risk
43 | 2. No input data has been validated (apart from adding . (dot) at the end of zones/records), so be careful
44 | 3. If unsure -> add https + basic auth in front of PDNS Manager directory
45 | 4. Feel free to contribute, correct the bugs, add extra functionality
46 |
47 | - - - -
48 |
49 | ### 2DO
50 | - [x] DDNS (via GET)
51 | - [x] DDNS (via POST)
52 | - [x] DDNS get IP from source request by default
53 | - [x] OTP can be turned off by setting secret to 'null' value: e.g. 'user' => array('password' => 'hash', 'secret' => null); and enabled/disabled via settings
54 | - [ ] Protect credentials/token against bruteforcing
55 | - [ ] Validate TXT record to have surrounding quotes
56 | - [ ] Implement TOKEN -> ZONE / RECORD policy
57 | - [ ] Implement USER -> ZONE / RECORD policy
58 | - [ ] Multi-user support (credentials, zones, records)
59 | - [ ] Validate content depending on the record type
60 | - [ ] Pass status codes from PowerDNS API back to application API
61 | - [ ] Cleanup
62 | - [ ] PDNS_Helper -> prepare() -> array
63 | - [ ] Minify everything
64 | - [ ] Possibility to edit settings from the GUI (???)
65 | - [ ] Possibility to edit credentials from the GUI (???)
66 |
67 | - - - -
68 |
69 | ### High level logic overview
70 | 
71 |
72 | - - - -
73 |
74 | ### Screenshots
75 | #### Sign in window
76 | 
77 |
78 | - - - -
79 |
80 | #### Sign up window
81 | 
82 |
83 | - - - -
84 |
85 | #### Records editing window
86 | 
87 |
88 | - - - -
89 |
90 | ### Setup
91 | 1. Clone the project
92 | 2. Configure the web server (NGINX snippet) + enable PHP
93 | ```
94 | location /pdns/api2 {
95 | root /var/www/pdns.yourdns.com/pdns/api2;
96 | try_files $uri /pdns/api2/index.php$is_args$args;
97 | }
98 |
99 | location / {
100 | try_files $uri $uri/ =404;
101 | }
102 | ```
103 | 3. Fetch dependencies using composer (check composer.json to see what is needed)
104 | 4. Navigate to https://pdns.yourdns.com -> you should see the login prompt and be able to "sign up"
105 | 5. Save the received credentials to the '-sample.php' files and rename the files (credentials-sample.php and settings-sample.php) by removing '-sample'
106 | 6. Enjoy...
107 |
--------------------------------------------------------------------------------
/api2/auth_helper.php:
--------------------------------------------------------------------------------
1 | 0) {
11 | require __DIR__ . '/../vendor/autoload.php';
12 | printf("\nNew AUTH_KEY: %s\n\n", Key::createNewRandomKey()->saveToAsciiSafeString());
13 | exit;
14 | }
15 |
16 |
17 |
18 | class AUTH_Helper {
19 |
20 | private $key;
21 |
22 | function __construct() {
23 | $this->key = Key::loadFromAsciiSafeString(AUTH_KEY);
24 | }
25 |
26 |
27 | function test($something = null) {
28 |
29 | return $something;
30 |
31 | }
32 |
33 |
34 | function generate_key() {
35 | $key = Key::createNewRandomKey();
36 | return $key->saveToAsciiSafeString();
37 | }
38 |
39 |
40 | function encrypt($text) {
41 | return Crypto::encrypt(sprintf('%s', $text), $this->key, $raw_binary = false);
42 | }
43 |
44 | function decrypt($text) {
45 | return Crypto::decrypt(sprintf('%s', $text), $this->key, $raw_binary = false);
46 | }
47 |
48 |
49 | function check_auth($request) {
50 |
51 | $auth_status = false;
52 |
53 |
54 |
55 | $cookies = new \Slim\Psr7\Cookies($request->getCookieParams());
56 | $auth_cookie = $cookies->get('pdns_auth_cookie');
57 |
58 | if ($auth_cookie) {
59 |
60 | $auth_cookie_ary = json_decode($this->decrypt($auth_cookie), true);
61 |
62 |
63 | $ts = $auth_cookie_ary['ts'];
64 | $username = $auth_cookie_ary['username'];
65 | $password = $auth_cookie_ary['password'];
66 |
67 |
68 | ## Check TS
69 | if (($ts + AUTH_LIFE + 1) >= time()) { ## TS is valid
70 |
71 | if (array_key_exists($username, AUTH_HASH) && AUTH_HASH[$username]['password'] == md5($password)) {
72 |
73 | $auth_status = true;
74 |
75 | $auth_cookie =
76 | $this->encrypt(
77 | json_encode([
78 | 'ts' => time(),
79 | 'username' => $username,
80 | 'password' => $password
81 | ])
82 | );
83 |
84 | } else {
85 |
86 | $auth_cookie = null;
87 |
88 | }
89 |
90 |
91 | } else {
92 |
93 | $auth_cookie = null;
94 |
95 | }
96 |
97 |
98 |
99 | } else {
100 |
101 | $auth_cookie = null;
102 |
103 | }
104 |
105 |
106 |
107 |
108 | return [ $auth_status, $auth_cookie, 'auth_status' => $auth_status, 'auth_cookie' => $auth_cookie ];
109 |
110 | }
111 |
112 |
113 |
114 | function is_totp_ok($username, $otp) {
115 | if (OTP_ENABLED) {
116 | if (array_key_exists('secret', AUTH_HASH[$username])) {
117 | if (AUTH_HASH[$username]['secret'] === null) {
118 | return true;
119 | } else {
120 | if (class_exists('OTPHP\TOTP')) {
121 | $totp = new \OTPHP\TOTP(null, AUTH_HASH[$username]['secret']);
122 | return $totp->verify($otp);
123 | } else {
124 | return false;
125 | }
126 | }
127 | } else {
128 | return true;
129 | }
130 | } else {
131 | return true;
132 | }
133 | }
134 |
135 |
136 | function signup($username) {
137 |
138 | if (OTP_ENABLED === true && class_exists('OTPHP\TOTP')) {
139 |
140 | $totp = new \OTPHP\TOTP();
141 |
142 | $otp_secret = $totp->getSecret();
143 | $totp = new \OTPHP\TOTP($username, $otp_secret);
144 | $otp_url = $totp->getProvisioningUri();
145 |
146 |
147 | $qrCode = new QrCode($otp_url);
148 | $otp_qr = $qrCode->writeDataUri();
149 |
150 |
151 | return [ $otp_secret, $otp_url, $otp_qr ];
152 |
153 | } else {
154 |
155 | return [ 'TOTP disabled and/or not installed', 'TOTP disabled and/or not installed', null ];
156 |
157 | }
158 |
159 | }
160 |
161 |
162 |
163 | function do_auth($username, $password, $otp) {
164 |
165 | $auth_status = false;
166 | $auth_cookie = null;
167 |
168 | if (array_key_exists($username, AUTH_HASH) && AUTH_HASH[$username]['password'] == md5($password) && $this->is_totp_ok($username, $otp) === true) {
169 |
170 | $auth_status = true;
171 |
172 | $auth_cookie =
173 | $this->encrypt(
174 | json_encode([
175 | 'ts' => time(),
176 | 'username' => $username,
177 | 'password' => $password
178 | ])
179 | );
180 |
181 | }
182 |
183 |
184 | return [ $auth_status, $auth_cookie ];
185 |
186 | }
187 |
188 |
189 | }
190 |
191 |
192 |
--------------------------------------------------------------------------------
/res/js/toastr.min.js:
--------------------------------------------------------------------------------
1 | !function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return g({type:O.error,iconClass:m().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=m()),v=e("#"+t.containerId),v.length?v:(n&&(v=d(t)),v)}function o(e,t,n){return g({type:O.info,iconClass:m().iconClasses.info,message:e,optionsOverride:n,title:t})}function s(e){C=e}function i(e,t,n){return g({type:O.success,iconClass:m().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return g({type:O.warning,iconClass:m().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e,t){var o=m();v||n(o),u(e,o,t)||l(o)}function c(t){var o=m();return v||n(o),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function l(t){for(var n=v.children(),o=n.length-1;o>=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e(""),M=e(""),B=e(""),q=e(""),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.4",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)});
2 | //# sourceMappingURL=toastr.js.map
3 |
--------------------------------------------------------------------------------
/res/css/toastr.min.css:
--------------------------------------------------------------------------------
1 | .toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#FFF}.toast-message a:hover{color:#CCC;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#FFF;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#FFF;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51A351}.toast-error{background-color:#BD362F}.toast-info{background-color:#2F96B4}.toast-warning{background-color:#F89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}}
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PDNS Manager
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
79 |
80 |
81 |
82 |
83 |
84 |
124 |
125 |
126 |
127 |
128 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
267 |
268 |
269 |
270 |
--------------------------------------------------------------------------------
/api2/index.php:
--------------------------------------------------------------------------------
1 | setBasePath(URL_PREFIX);
23 |
24 |
25 | $app->get('/test[/{something}]', function (Request $request, Response $response, $args) {
26 |
27 | global $PDNS, $AUTH;
28 |
29 |
30 | // $payload = json_encode([$AUTH->test($args['something'])]);
31 | $payload = json_encode($AUTH->check_auth($request)['auth_status']);
32 | // $payload = json_encode($AUTH->generate_key());
33 |
34 |
35 | $response->getBody()->write($payload);
36 | return $response->withHeader('Content-Type', 'application/json');
37 |
38 | });
39 |
40 |
41 |
42 |
43 |
44 | $app->post('/signup', function (Request $request, Response $response) {
45 |
46 | global $AUTH;
47 |
48 | $params = $request->getParsedBody();
49 |
50 |
51 | $password_hash = md5($params['password']);
52 | list($otp_secret, $otp_url, $otp_qr) = $AUTH->signup($params['username']);
53 |
54 |
55 | $payload = json_encode(['password_hash' => $password_hash, 'otp_secret' => $otp_secret, 'otp_url' => $otp_url, 'otp_qr' => $otp_qr ]);
56 |
57 |
58 | $response->getBody()->write($payload);
59 | return $response->withHeader('Content-Type', 'application/json');
60 |
61 |
62 | });
63 |
64 |
65 |
66 |
67 | $app->get('/check_auth', function (Request $request, Response $response, $args) {
68 |
69 | global $AUTH;
70 |
71 |
72 | list($auth_status, $auth_cookie) = $AUTH->check_auth($request);
73 |
74 |
75 | $payload = json_encode(['auth_status' => $auth_status, 'auth_cookie' => $auth_cookie]);
76 |
77 |
78 |
79 |
80 | $response->getBody()->write($payload);
81 | return $response->withHeader('Content-Type', 'application/json');
82 |
83 |
84 | });
85 |
86 |
87 |
88 | $app->post('/do_auth', function (Request $request, Response $response) {
89 |
90 | global $AUTH;
91 |
92 | $params = $request->getParsedBody();
93 |
94 | $auth_status = false;
95 | $auth_cookie = null;
96 |
97 | if (array_key_exists('username', $params) && array_key_exists('password', $params) && array_key_exists('otp', $params)) {
98 |
99 | list($auth_status, $auth_cookie) = $AUTH->do_auth($params['username'], $params['password'], $params['otp']);
100 |
101 | }
102 |
103 |
104 |
105 | $payload = json_encode(['auth_status' => $auth_status, 'auth_cookie' => $auth_cookie]);
106 |
107 |
108 | $response->getBody()->write($payload);
109 | return $response->withHeader('Content-Type', 'application/json');
110 |
111 |
112 | });
113 |
114 |
115 |
116 | ## DDNS via POST
117 | $app->post('/ddns', function (Request $request, Response $response) {
118 |
119 | global $PDNS, $AUTH;
120 |
121 | $params = $request->getParsedBody();
122 |
123 |
124 | $status = $PDNS->do_ddns($params);
125 |
126 |
127 | $response->getBody()->write($status);
128 | return $response->withHeader('Content-Type', 'text/plain');
129 |
130 | });
131 |
132 |
133 | ## DDNS via GET
134 | $app->get('/ddns[/{get_params:.*}]', function (Request $request, Response $response, $args) {
135 |
136 | global $PDNS, $AUTH;
137 |
138 | $params = array();
139 | $get_params = explode('/', $args['get_params']);
140 |
141 |
142 | foreach ($get_params as $kv_pair) {
143 | if (preg_match('/^(.+)=(.+)$/', $kv_pair, $kv_ary)) {
144 | $key = $kv_ary[1];
145 | $val = $kv_ary[2];
146 | $params[$key] = $val;
147 | }
148 | }
149 |
150 |
151 | $status = $PDNS->do_ddns($params);
152 |
153 | $response->getBody()->write($status);
154 | return $response->withHeader('Content-Type', 'text/plain');
155 |
156 |
157 | });
158 |
159 |
160 |
161 | $app->post('/globals_init', function (Request $request, Response $response) {
162 |
163 | global $PDNS, $AUTH;
164 |
165 | $settings = array();
166 |
167 |
168 | if ($AUTH->check_auth($request)['auth_status'] === true) {
169 |
170 | $settings['AUTH_INTERVAL'] = AUTH_INTERVAL;
171 | $settings['ZONE_DEFAULTS'] = ZONE_DEFAULTS;
172 |
173 | }
174 |
175 |
176 | $payload = json_encode($settings);
177 |
178 |
179 | $response->getBody()->write($payload);
180 | return $response->withHeader('Content-Type', 'application/json');
181 |
182 |
183 | });
184 |
185 |
186 |
187 | $app->post('/remove_record', function (Request $request, Response $response) {
188 |
189 | global $PDNS, $AUTH;
190 |
191 | if ($AUTH->check_auth($request)['auth_status'] === true) {
192 |
193 | $params = $request->getParsedBody();
194 |
195 | $PDNS->remove_record($params['zone'], $params['type'], $params['name']);
196 |
197 |
198 | $payload = json_encode(['default' => true]);
199 |
200 | } else {
201 |
202 | $payload = json_encode(['auth_status' => false]);
203 |
204 | }
205 |
206 |
207 | $response->getBody()->write($payload);
208 | return $response->withHeader('Content-Type', 'application/json');
209 |
210 |
211 | });
212 |
213 |
214 |
215 | $app->post('/save_records', function (Request $request, Response $response) {
216 |
217 | global $PDNS, $AUTH;
218 |
219 | if ($AUTH->check_auth($request)['auth_status'] === true) {
220 |
221 | $params = $request->getParsedBody();
222 |
223 | $PDNS->save_records($params);
224 |
225 |
226 | $payload = json_encode(['default' => true]);
227 |
228 | } else {
229 |
230 | $payload = json_encode(['auth_status' => false]);
231 |
232 | }
233 |
234 |
235 | $response->getBody()->write($payload);
236 | return $response->withHeader('Content-Type', 'application/json');
237 |
238 |
239 | });
240 |
241 |
242 |
243 | $app->post('/remove_zone/{zone}', function (Request $request, Response $response, $args) {
244 |
245 | global $PDNS, $AUTH;
246 |
247 |
248 | if ($AUTH->check_auth($request)['auth_status'] === true) {
249 |
250 | $PDNS->remove_zone($args['zone']);
251 |
252 |
253 | $payload = json_encode(['default' => true]);
254 |
255 | } else {
256 |
257 | $payload = json_encode(['auth_status' => false]);
258 |
259 | }
260 |
261 |
262 | $response->getBody()->write($payload);
263 | return $response->withHeader('Content-Type', 'application/json');
264 |
265 | });
266 |
267 |
268 |
269 |
270 | $app->post('/add_zone', function (Request $request, Response $response) {
271 |
272 | global $PDNS, $AUTH;
273 |
274 |
275 | if ($AUTH->check_auth($request)['auth_status'] === true) {
276 |
277 | $params = $request->getParsedBody();
278 |
279 | $PDNS->add_zone($params);
280 |
281 | $payload = json_encode(['default' => true]);
282 |
283 | } else {
284 |
285 | $payload = json_encode(['auth_status' => false]);
286 |
287 | }
288 |
289 |
290 | $response->getBody()->write($payload);
291 | return $response->withHeader('Content-Type', 'application/json');
292 |
293 |
294 | });
295 |
296 |
297 | $app->post('/get_zones', function (Request $request, Response $response, $args) {
298 |
299 | global $PDNS, $AUTH;
300 |
301 |
302 | if ($AUTH->check_auth($request)['auth_status'] === true) {
303 |
304 | $payload = $PDNS->get_zones();
305 |
306 | } else {
307 |
308 | $payload = json_encode(['auth_status' => false]);
309 |
310 | }
311 |
312 |
313 | $response->getBody()->write($payload);
314 | return $response->withHeader('Content-Type', 'application/json');
315 |
316 | });
317 |
318 |
319 |
320 | $app->post('/get_records/{zone}', function (Request $request, Response $response, $args) {
321 |
322 | global $PDNS, $AUTH;
323 |
324 | if ($AUTH->check_auth($request)['auth_status'] === true) {
325 |
326 | $payload = $PDNS->get_records($args['zone']);
327 |
328 | } else {
329 |
330 | $payload = json_encode(['auth_status' => false]);
331 |
332 | }
333 |
334 |
335 | $response->getBody()->write($payload);
336 | return $response->withHeader('Content-Type', 'application/json');
337 |
338 | });
339 |
340 |
341 |
342 | // DEFAULT - catch all
343 | $app->get('/{path:.*}', function ($request, $response, array $args) {
344 | $payload = json_encode(['default' => true]);
345 | $response->getBody()->write($payload);
346 | return $response->withHeader('Content-Type', 'application/json');
347 | });
348 |
349 |
350 |
351 |
352 | $app->run();
--------------------------------------------------------------------------------
/res/js/api.js:
--------------------------------------------------------------------------------
1 |
2 | var GLOBALS = {};
3 | var row_id = 1;
4 |
5 |
6 | function globals_init() {
7 |
8 | $.post('api2/globals_init', function(data) {
9 | GLOBALS = data;
10 | });
11 |
12 | }
13 |
14 |
15 | function signup() {
16 |
17 |
18 | var form_data = $('#auth_form').serializeArray();
19 | var all_valid = true;
20 |
21 | for (let key in form_data) {
22 |
23 | if (form_data[key].name == 'username') {
24 | if (form_data[key].value == '') {
25 | all_valid = false;
26 | $('.auth-form-username').toggleClass('is-invalid', true);
27 | } else {
28 | $('.auth-form-username').removeClass('is-invalid');
29 | }
30 | } else if (form_data[key].name == 'password') {
31 | if (form_data[key].value == '') {
32 | all_valid = false;
33 | $('.auth-form-password').toggleClass('is-invalid', true);
34 | } else {
35 | $('.auth-form-password').removeClass('is-invalid');
36 | }
37 | }
38 |
39 |
40 | }
41 |
42 |
43 | $('.auth-form-otp').removeClass('is-invalid');
44 |
45 |
46 | if (all_valid) {
47 |
48 | $.post('api2/signup', form_data, function(data) {
49 |
50 | $('input#passwordHash').val(data.password_hash);
51 | $('input#otpSecret').val(data.otp_secret);
52 | $('input#otpURL').val(data.otp_url);
53 | $('img#otpIMG').attr('src', data.otp_qr);
54 |
55 |
56 | $('.signup-div').show();
57 |
58 | });
59 |
60 | }
61 |
62 | }
63 |
64 |
65 |
66 | function update_cookie(content) {
67 | Cookies.set('pdns_auth_cookie', content, { secure: true, expires: 365 });
68 | }
69 |
70 |
71 | function remove_cookie() {
72 | Cookies.remove('pdns_auth_cookie', { secure: true });
73 | }
74 |
75 |
76 |
77 | function sign_out() {
78 | remove_cookie();
79 | location.reload();
80 | }
81 |
82 |
83 |
84 | function auth_loop() {
85 |
86 | var delay = GLOBALS['AUTH_INTERVAL'] * 1000;
87 |
88 | setTimeout( function() {
89 |
90 | $.get('api2/check_auth', function(data) {
91 |
92 | if (data.auth_status) {
93 |
94 | update_cookie(data.auth_cookie);
95 |
96 | } else {
97 |
98 | sign_out();
99 |
100 | }
101 |
102 | auth_loop();
103 |
104 | });
105 |
106 |
107 |
108 | }, delay);
109 |
110 |
111 | }
112 |
113 |
114 |
115 | function do_auth() {
116 |
117 | var form_data = $('#auth_form').serializeArray();
118 |
119 | $.post('api2/do_auth', form_data, function(data) {
120 |
121 | if (data.auth_status) {
122 |
123 | update_cookie(data.auth_cookie);
124 | location.reload();
125 |
126 | } else {
127 |
128 | remove_cookie();
129 |
130 | $('.auth-form-username').toggleClass('is-invalid', true);
131 | $('.auth-form-password').toggleClass('is-invalid', true);
132 | $('.auth-form-otp').toggleClass('is-invalid', true);
133 |
134 | }
135 |
136 |
137 |
138 | });
139 |
140 |
141 |
142 | return false;
143 |
144 | }
145 |
146 |
147 |
148 | function save_records() {
149 |
150 | var zone = $('#zones :selected').val();
151 | if (zone && zone != 0) {
152 |
153 | var form_data = $('#records_form').serializeArray();
154 |
155 | var all_valid = true;
156 |
157 | $('#records_form .record-data').each(function( index ) {
158 |
159 | if (this.value == '') {
160 | all_valid = false;
161 | $(this).toggleClass('is-invalid', true);
162 | } else {
163 | $(this).removeClass('is-invalid');
164 | }
165 |
166 | });
167 |
168 |
169 | if (all_valid) {
170 |
171 | $.post('api2/save_records', form_data, function(data) {
172 | get_records(zone);
173 | toastr.success('Records has been saved!');
174 | });
175 |
176 | }
177 |
178 |
179 | }
180 |
181 | }
182 |
183 |
184 | function remove_record(zone, type, name) {
185 |
186 | if (confirm('Proceed with the "' + type + '" type for "' + name + '" record removal?')) {
187 | $.post('api2/remove_record', { zone: zone, type: type, name: name }, function(data) {
188 | get_records(zone);
189 | });
190 | }
191 |
192 | }
193 |
194 |
195 |
196 | function remove_row(row_id) {
197 | $('#row_' + row_id).remove();
198 | }
199 |
200 |
201 | function remove_zone() {
202 | var zone = $('#zones :selected').val();
203 | if (zone && zone != 0) {
204 | if (confirm('Proceed with the "' + zone + '" zone removal?')) {
205 | $.post('api2/remove_zone/' + zone, function(data) {
206 | clear_records();
207 | refresh_zones();
208 | });
209 | }
210 | }
211 | }
212 |
213 |
214 |
215 | function refresh_records() {
216 | var zone = $('#zones :selected').val();
217 | if (zone && zone != 0) {
218 | get_records(zone);
219 | }
220 | refresh_zones(zone);
221 |
222 | toastr.info('Records has been re-loaded...');
223 |
224 | }
225 |
226 |
227 |
228 | function clear_records() {
229 | $('#records').empty();
230 | }
231 |
232 |
233 |
234 | function add_zone(params) {
235 | $.post('api2/add_zone', params, function (data) {
236 | clear_records();
237 | refresh_zones();
238 | toastr.success('The new zone [' + params['zone'] + '] has been created!');
239 | });
240 | }
241 |
242 |
243 | function get_records(zone) {
244 |
245 | $.post('api2/get_records/' + zone, function(data) {
246 |
247 |
248 | clear_records();
249 |
250 |
251 | for (let key in data) {
252 |
253 | // console.log(data[key]);
254 |
255 | var type = data[key].type;
256 | var name = data[key].name;
257 | var records = data[key].records;
258 | var ttl = data[key].ttl;
259 |
260 | // console.log(records);
261 |
262 |
263 | records.forEach(function(value, index, array) {
264 |
265 | var content = value.content;
266 |
267 | var record_tpl = ``;
274 |
275 | $('#records').append(record_tpl);
276 |
277 | row_id++;
278 |
279 | });
280 |
281 |
282 |
283 |
284 | }
285 |
286 |
287 | var add_record_tpl = `
288 |
289 |
290 |
291 |
`;
292 |
293 | $('#records').append(add_record_tpl);
294 |
295 |
296 | });
297 |
298 |
299 | }
300 |
301 | function set_type(row_id, type, zone) {
302 |
303 | var type_val = '';
304 | var name_attr = zone;
305 | var content_attr = '';
306 |
307 |
308 | if (type != '') {
309 |
310 | type_val = type;
311 |
312 | if (type == 'TXT') {
313 | content_attr = '"some text value surrounded by quotes"';
314 | } else if (type == 'A') {
315 | content_attr = '127.1.2.3';
316 | } else if (type == 'CNAME') {
317 | content_attr = 'domain01.' + zone;
318 | } else if (type == 'MX') {
319 | content_attr = '10 ' + zone;
320 | } else if (type == 'SRV') {
321 | name_attr = '_sip._udp.sip.' + zone;
322 | content_attr = 'prio[10] weight[1] port[5060] sip.' + zone;
323 | }
324 |
325 | }
326 |
327 |
328 | $('#type_field_' + row_id).val(type_val);
329 | $('#name_field_' + row_id).attr('placeholder', name_attr);
330 | $('#content_field_' + row_id).attr('placeholder', content_attr);
331 |
332 |
333 | }
334 |
335 |
336 |
337 | function add_record(zone) {
338 |
339 | var dh = $(document).height();
340 |
341 |
342 | var new_record_tpl = `
343 |
344 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
`;
359 |
360 |
361 | $(new_record_tpl).insertBefore( $('#add_record_btn_div') );
362 | row_id++;
363 |
364 |
365 |
366 | var diff = $(document).height() - dh;
367 | window.scrollBy(0, diff);
368 |
369 |
370 |
371 | }
372 |
373 |
374 |
375 | function refresh_zones(default_zone = null) {
376 |
377 | $.post('api2/get_zones', function(data) {
378 |
379 | var list = $('#zones').empty();
380 | list.append($('