mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-03-18 21:30:44 +01:00
More config generator
This commit is contained in:
156
docs/config.md
156
docs/config.md
@@ -137,20 +137,28 @@ using Docker Compose (i.e. `docker-compose.yml`):
|
||||
|
||||
## Config generator
|
||||
|
||||
This generator helps you configure your self-hosted ntfy instance. It's not fully featured, but it is a good starting point. Please refer to the relevant sections in the doc for more details.
|
||||
|
||||
<button type="button" id="cg-open-btn" class="cg-open-btn">Open config generator</button>
|
||||
<div id="cg-modal" class="cg-modal" style="display:none">
|
||||
<div class="cg-modal-backdrop"></div>
|
||||
<div class="cg-modal-dialog">
|
||||
<div class="cg-modal-header">
|
||||
<div class="cg-modal-header-left">
|
||||
<span class="cg-modal-title">Config generator</span>
|
||||
<span class="cg-modal-desc">This generator helps you configure your self-hosted ntfy instance. It's not fully featured, but it is a good starting point.</span>
|
||||
</div>
|
||||
<div class="cg-modal-header-actions">
|
||||
<button type="button" id="cg-reset-btn" class="cg-modal-reset" title="Reset all values">Reset</button>
|
||||
<button type="button" id="cg-close-btn" class="cg-modal-close" title="Close">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-modal-body">
|
||||
<div id="cg-left">
|
||||
<div class="cg-nav">
|
||||
<div class="cg-nav-tab active" data-panel="cg-panel-general">General</div>
|
||||
<div class="cg-nav-tab" data-panel="cg-panel-database" id="cg-nav-database" style="display:none">Database</div>
|
||||
<div class="cg-nav-tab" data-panel="cg-panel-auth" id="cg-nav-auth" style="display:none">Access Control</div>
|
||||
<div class="cg-nav-tab" data-panel="cg-panel-auth" id="cg-nav-auth" style="display:none">Users</div>
|
||||
<div class="cg-nav-tab" data-panel="cg-panel-cache" id="cg-nav-cache" style="display:none">Message Cache</div>
|
||||
<div class="cg-nav-tab" data-panel="cg-panel-attach" id="cg-nav-attach" style="display:none">Attachments</div>
|
||||
<div class="cg-nav-tab" data-panel="cg-panel-webpush" id="cg-nav-webpush" style="display:none">Web Push</div>
|
||||
@@ -158,129 +166,137 @@ using Docker Compose (i.e. `docker-compose.yml`):
|
||||
</div>
|
||||
<div class="cg-panels">
|
||||
<div class="cg-panel active" id="cg-panel-general">
|
||||
<div class="cg-field">
|
||||
<label>What's your ntfy service URL?</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>What URL will ntfy be reachable on?</label>
|
||||
<input type="text" data-key="base-url" placeholder="https://ntfy.example.com">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Listen address</label>
|
||||
<input type="text" data-key="listen-http" placeholder=":80">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Will ntfy run behind a proxy (e.g. nginx, Caddy, Apache2)?</label>
|
||||
<div class="cg-radio-group">
|
||||
<label><input type="radio" name="cg-proxy" value="no" checked> No</label>
|
||||
<label><input type="radio" name="cg-proxy" value="yes"> Yes</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Will ntfy run behind a proxy (e.g. nginx, Caddy)? <a href="/config/#behind-a-proxy-tls-etc" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-proxy" value="no" checked><span>No</span></label>
|
||||
<label><input type="radio" name="cg-proxy" value="yes"><span>Yes</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Is this an open server or a private server?</label>
|
||||
<div class="cg-radio-group">
|
||||
<label><input type="radio" name="cg-server-type" value="open" checked> Open (anyone can read/write)</label>
|
||||
<label><input type="radio" name="cg-server-type" value="private"> Private (requires authentication)</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Is this an open or private server? <a href="/config/#access-control" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-server-type" value="open" checked><span>Open</span></label>
|
||||
<label><input type="radio" name="cg-server-type" value="private"><span>Private</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Will iOS/iPhone users use this server?</label>
|
||||
<div class="cg-radio-group">
|
||||
<label><input type="radio" name="cg-ios" value="no" checked> No</label>
|
||||
<label><input type="radio" name="cg-ios" value="yes"> Yes</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Will iOS/iPhone users use this server? <a href="/config/#ios-instant-notifications" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-ios" value="no" checked><span>No</span></label>
|
||||
<label><input type="radio" name="cg-ios" value="yes"><span>Yes</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Do you want to use ntfy as a UnifiedPush distributor?</label>
|
||||
<div class="cg-radio-group">
|
||||
<label><input type="radio" name="cg-unifiedpush" value="no" checked> No</label>
|
||||
<label><input type="radio" name="cg-unifiedpush" value="yes"> Yes</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Do you want to use ntfy as a UnifiedPush distributor? <a href="/config/#example-unifiedpush" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-unifiedpush" value="no" checked><span>No</span></label>
|
||||
<label><input type="radio" name="cg-unifiedpush" value="yes"><span>Yes</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Which features do you want to enable?</label>
|
||||
<div class="cg-feature-grid">
|
||||
<label><input type="checkbox" id="cg-feat-auth"> User management and access control</label>
|
||||
<label><input type="checkbox" id="cg-feat-cache"> Persistent message cache</label>
|
||||
<label><input type="checkbox" id="cg-feat-attach"> Attachments</label>
|
||||
<label><input type="checkbox" id="cg-feat-webpush"> Web push</label>
|
||||
<label><input type="checkbox" id="cg-feat-smtp-out"> Email notifications (outgoing)</label>
|
||||
<label><input type="checkbox" id="cg-feat-smtp-in"> Email publishing (incoming)</label>
|
||||
<div class="cg-feature-row"><label><input type="checkbox" id="cg-feat-auth"> User management and access control</label><button type="button" class="cg-btn-configure" data-panel="cg-panel-auth" style="display:none">Configure</button></div>
|
||||
<div class="cg-feature-row"><label><input type="checkbox" id="cg-feat-cache"> Persistent message cache</label><button type="button" class="cg-btn-configure" data-panel="cg-panel-cache" style="display:none">Configure</button></div>
|
||||
<div class="cg-feature-row"><label><input type="checkbox" id="cg-feat-attach"> Attachments</label><button type="button" class="cg-btn-configure" data-panel="cg-panel-attach" style="display:none">Configure</button></div>
|
||||
<div class="cg-feature-row"><label><input type="checkbox" id="cg-feat-webpush"> Web push</label><button type="button" class="cg-btn-configure" data-panel="cg-panel-webpush" style="display:none">Configure</button></div>
|
||||
<div class="cg-feature-row"><label><input type="checkbox" id="cg-feat-smtp-out"> Email notifications</label><button type="button" class="cg-btn-configure" data-panel="cg-panel-email" style="display:none">Configure</button></div>
|
||||
<div class="cg-feature-row"><label><input type="checkbox" id="cg-feat-smtp-in"> Email publishing</label><button type="button" class="cg-btn-configure" data-panel="cg-panel-email" style="display:none">Configure</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-field" id="cg-wizard-db" style="display:none">
|
||||
<label>Which database backend would you like to use?</label>
|
||||
<div class="cg-radio-group">
|
||||
<label><input type="radio" name="cg-db-type" value="sqlite" checked> SQLite</label>
|
||||
<label><input type="radio" name="cg-db-type" value="postgres"> PostgreSQL</label>
|
||||
<div class="cg-field cg-inline-field" id="cg-wizard-db" style="display:none">
|
||||
<label>Which database backend? <a href="/config/#database-options" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-db-type" value="sqlite" checked><span>SQLite</span></label>
|
||||
<label><input type="radio" name="cg-db-type" value="postgres"><span>PostgreSQL</span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-panel" id="cg-panel-auth">
|
||||
<div class="cg-field">
|
||||
<label>Auth file</label>
|
||||
<div class="cg-panel-desc">Configure user management, access control, and provisioned users/ACLs. See <a href="/config/#access-control" target="_blank">access control</a> for details.</div>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Where should the user database be stored?</label>
|
||||
<input type="text" data-key="auth-file" placeholder="/var/lib/ntfy/auth.db">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Default access</label>
|
||||
<select data-key="auth-default-access">
|
||||
<option value="">-- select --</option>
|
||||
<option value="read-write">read-write</option>
|
||||
<option value="read-only">read-only</option>
|
||||
<option value="write-only">write-only</option>
|
||||
<option value="deny-all">deny-all</option>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>What is the default access policy? <a href="/config/#access-control" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<select id="cg-default-access-select">
|
||||
<option value="read-write" selected>Read & Write</option>
|
||||
<option value="read-only">Read Only</option>
|
||||
<option value="write-only">Write Only</option>
|
||||
<option value="deny-all">Deny All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="cg-checkbox">
|
||||
<input type="checkbox" data-key="enable-login" id="cg-enable-login">
|
||||
<label for="cg-enable-login">Enable login</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Enable login page?</label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-enable-login" value="no" checked><span>No</span></label>
|
||||
<label><input type="radio" name="cg-enable-login" value="yes"><span>Yes</span></label>
|
||||
</div>
|
||||
<div class="cg-checkbox">
|
||||
<input type="checkbox" data-key="enable-signup" id="cg-enable-signup">
|
||||
<label for="cg-enable-signup">Enable signup</label>
|
||||
</div>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Enable signup?</label>
|
||||
<div class="cg-btn-group">
|
||||
<label><input type="radio" name="cg-enable-signup" value="no" checked><span>No</span></label>
|
||||
<label><input type="radio" name="cg-enable-signup" value="yes"><span>Yes</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" data-key="auth-default-access">
|
||||
<input type="checkbox" data-key="enable-login" id="cg-enable-login-hidden" style="display:none">
|
||||
<input type="checkbox" data-key="enable-signup" id="cg-enable-signup-hidden" style="display:none">
|
||||
<div class="cg-field">
|
||||
<label>Provisioned users</label>
|
||||
<label>Provisioned users <a href="/config/#users-and-roles" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-repeatable-container" id="cg-auth-users-container"></div>
|
||||
<button type="button" class="cg-btn-add" data-add-type="user">+ Add user</button>
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Provisioned ACLs</label>
|
||||
<label>Provisioned ACLs <a href="/config/#access-control-list-acl" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-repeatable-container" id="cg-auth-acls-container"></div>
|
||||
<button type="button" class="cg-btn-add" data-add-type="acl">+ Add ACL</button>
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Provisioned tokens</label>
|
||||
<label>Provisioned tokens <a href="/config/#access-tokens" target="_blank" class="cg-help"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.496 6.033h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286a.237.237 0 0 0 .241.247m2.325 6.443c.61 0 1.029-.394 1.029-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94 0 .533.425.927 1.01.927z"/></svg></a></label>
|
||||
<div class="cg-repeatable-container" id="cg-auth-tokens-container"></div>
|
||||
<button type="button" class="cg-btn-add" data-add-type="token">+ Add token</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-panel" id="cg-panel-cache">
|
||||
<div class="cg-field" id="cg-cache-file-field">
|
||||
<label>Cache file</label>
|
||||
<div class="cg-panel-desc">Configure the message cache to allow devices to retrieve missed notifications. See <a href="/config/#message-cache" target="_blank">message cache</a> for details.</div>
|
||||
<div class="cg-field cg-inline-field" id="cg-cache-file-field">
|
||||
<label>Where should the cache be stored?</label>
|
||||
<input type="text" data-key="cache-file" placeholder="/var/cache/ntfy/cache.db">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Cache duration</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>How long should messages be cached?</label>
|
||||
<input type="text" data-key="cache-duration" placeholder="12h">
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-panel" id="cg-panel-attach">
|
||||
<div class="cg-field">
|
||||
<label>Cache directory</label>
|
||||
<div class="cg-panel-desc">Allow users to upload and attach files to notifications. See <a href="/config/#attachments" target="_blank">attachments</a> for details.</div>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Where should attachments be stored?</label>
|
||||
<input type="text" data-key="attachment-cache-dir" placeholder="/var/cache/ntfy/attachments">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>File size limit</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Max file size per attachment?</label>
|
||||
<input type="text" data-key="attachment-file-size-limit" placeholder="15M">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Total size limit</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>Total attachment storage limit?</label>
|
||||
<input type="text" data-key="attachment-total-size-limit" placeholder="5G">
|
||||
</div>
|
||||
<div class="cg-field">
|
||||
<label>Expiry duration</label>
|
||||
<div class="cg-field cg-inline-field">
|
||||
<label>How long before attachments expire?</label>
|
||||
<input type="text" data-key="attachment-expiry-duration" placeholder="3h">
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-panel" id="cg-panel-webpush">
|
||||
<div class="cg-panel-desc">Enable browser push notifications via the Web Push API. VAPID keys are generated automatically. See <a href="/config/#web-push" target="_blank">web push</a> for details.</div>
|
||||
<div class="cg-field">
|
||||
<label>Public key</label>
|
||||
<input type="text" data-key="web-push-public-key" placeholder="Public key" readonly>
|
||||
@@ -300,6 +316,7 @@ using Docker Compose (i.e. `docker-compose.yml`):
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-panel" id="cg-panel-email">
|
||||
<div class="cg-panel-desc">Configure outgoing email notifications and/or incoming email publishing. See <a href="/config/#e-mail-notifications" target="_blank">email notifications</a> and <a href="/config/#e-mail-publishing" target="_blank">email publishing</a> for details.</div>
|
||||
<div id="cg-email-out-section" style="display:none">
|
||||
<div class="cg-field"><label><strong>Outgoing (notifications)</strong></label></div>
|
||||
<div class="cg-field">
|
||||
@@ -336,6 +353,7 @@ using Docker Compose (i.e. `docker-compose.yml`):
|
||||
</div>
|
||||
</div>
|
||||
<div class="cg-panel" id="cg-panel-database">
|
||||
<div class="cg-panel-desc">Configure the PostgreSQL connection. See <a href="/config/#postgresql-experimental" target="_blank">PostgreSQL</a> for details.</div>
|
||||
<div class="cg-field">
|
||||
<label>Database URL</label>
|
||||
<input type="text" data-key="database-url" placeholder="postgres://user:pass@host:5432/ntfy">
|
||||
|
||||
217
docs/static/css/config-generator.css
vendored
217
docs/static/css/config-generator.css
vendored
@@ -53,9 +53,45 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cg-modal-header-left {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cg-modal-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cg-modal-desc {
|
||||
font-size: 0.75rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.cg-modal-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cg-modal-reset {
|
||||
background: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
padding: 4px 12px;
|
||||
font-family: inherit;
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.cg-modal-reset:hover {
|
||||
color: #333;
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
.cg-modal-close {
|
||||
@@ -134,6 +170,30 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cg-panel-desc {
|
||||
font-size: 0.75rem;
|
||||
color: #888;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.cg-panel-desc a {
|
||||
color: var(--md-primary-fg-color);
|
||||
}
|
||||
|
||||
.cg-help {
|
||||
color: var(--md-primary-fg-color);
|
||||
text-decoration: none;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.cg-help:hover {
|
||||
color: var(--md-primary-fg-color--dark, #2a6e5f);
|
||||
}
|
||||
|
||||
/* Right panel */
|
||||
#cg-right {
|
||||
flex: 1;
|
||||
@@ -195,6 +255,8 @@
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cg-output-wrap pre {
|
||||
@@ -207,7 +269,7 @@
|
||||
overflow-x: auto;
|
||||
font-size: 0.76rem;
|
||||
line-height: 1.5;
|
||||
min-height: 100px;
|
||||
flex: 1;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
@@ -228,11 +290,16 @@
|
||||
|
||||
/* Form fields */
|
||||
.cg-field {
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.cg-field:last-child {
|
||||
margin-bottom: 0;
|
||||
.cg-field:nth-child(odd) {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.cg-field:nth-child(even) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.cg-field > label {
|
||||
@@ -301,12 +368,98 @@
|
||||
accent-color: var(--md-primary-fg-color);
|
||||
}
|
||||
|
||||
/* Inline field: label + control side by side */
|
||||
.cg-inline-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.cg-inline-field > label {
|
||||
margin-bottom: 0;
|
||||
width: 60%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cg-inline-field > input[type="text"],
|
||||
.cg-inline-field > select {
|
||||
padding: 4px 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-family: inherit;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.4;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.cg-inline-field > input[type="text"]:focus,
|
||||
.cg-inline-field > select:focus {
|
||||
border-color: var(--md-primary-fg-color);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(51, 133, 116, 0.15);
|
||||
}
|
||||
|
||||
/* Button group toggle */
|
||||
.cg-btn-group {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cg-btn-group label {
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cg-btn-group input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cg-btn-group span {
|
||||
display: block;
|
||||
padding: 4px 14px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
border: 1px solid #ccc;
|
||||
color: #555;
|
||||
background: #fff;
|
||||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.cg-btn-group label:first-child span {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.cg-btn-group label:last-child span {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.cg-btn-group label + label span {
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.cg-btn-group input[type="radio"]:checked + span {
|
||||
background: var(--md-primary-fg-color);
|
||||
color: #fff;
|
||||
border-color: var(--md-primary-fg-color);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cg-feature-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.cg-feature-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cg-feature-grid label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -319,6 +472,24 @@
|
||||
accent-color: var(--md-primary-fg-color);
|
||||
}
|
||||
|
||||
.cg-btn-configure {
|
||||
background: none;
|
||||
border: 1px solid var(--md-primary-fg-color);
|
||||
border-radius: 10px;
|
||||
color: var(--md-primary-fg-color);
|
||||
font-size: 0.68rem;
|
||||
font-family: inherit;
|
||||
padding: 1px 10px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.cg-btn-configure:hover {
|
||||
background: var(--md-primary-fg-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Repeatable rows */
|
||||
.cg-repeatable-row {
|
||||
display: flex;
|
||||
@@ -392,6 +563,20 @@ body[data-md-color-scheme="slate"] .cg-modal-title {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-modal-desc {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-modal-reset {
|
||||
border-color: #555;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-modal-reset:hover {
|
||||
border-color: #888;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-modal-close {
|
||||
color: #777;
|
||||
}
|
||||
@@ -442,6 +627,18 @@ body[data-md-color-scheme="slate"] .cg-output-wrap pre {
|
||||
border-color: #444;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-field:nth-child(odd) {
|
||||
background: #232334;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-field:nth-child(even) {
|
||||
background: #1e1e2e;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-panel-desc {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-field > label {
|
||||
color: #aaa;
|
||||
}
|
||||
@@ -454,6 +651,18 @@ body[data-md-color-scheme="slate"] .cg-field select {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-btn-group span {
|
||||
background: #2a2a3a;
|
||||
border-color: #555;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-btn-group input[type="radio"]:checked + span {
|
||||
background: var(--md-primary-fg-color);
|
||||
color: #fff;
|
||||
border-color: var(--md-primary-fg-color);
|
||||
}
|
||||
|
||||
body[data-md-color-scheme="slate"] .cg-checkbox label {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
166
docs/static/js/config-generator.js
vendored
166
docs/static/js/config-generator.js
vendored
@@ -4,38 +4,32 @@
|
||||
|
||||
var CONFIG = [
|
||||
{ key: "base-url", env: "NTFY_BASE_URL", section: "basic" },
|
||||
{ key: "listen-http", env: "NTFY_LISTEN_HTTP", section: "basic", def: ":80" },
|
||||
{ key: "behind-proxy", env: "NTFY_BEHIND_PROXY", section: "basic", type: "bool" },
|
||||
{ key: "database-url", env: "NTFY_DATABASE_URL", section: "database" },
|
||||
{ key: "auth-file", env: "NTFY_AUTH_FILE", section: "auth", def: "/var/lib/ntfy/auth.db" },
|
||||
{ key: "auth-default-access", env: "NTFY_AUTH_DEFAULT_ACCESS", section: "auth" },
|
||||
{ key: "auth-file", env: "NTFY_AUTH_FILE", section: "auth" },
|
||||
{ key: "auth-default-access", env: "NTFY_AUTH_DEFAULT_ACCESS", section: "auth", def: "read-write" },
|
||||
{ key: "enable-login", env: "NTFY_ENABLE_LOGIN", section: "auth", type: "bool" },
|
||||
{ key: "enable-signup", env: "NTFY_ENABLE_SIGNUP", section: "auth", type: "bool" },
|
||||
{ key: "attachment-cache-dir", env: "NTFY_ATTACHMENT_CACHE_DIR", section: "attach", def: "/var/cache/ntfy/attachments" },
|
||||
{ key: "attachment-cache-dir", env: "NTFY_ATTACHMENT_CACHE_DIR", section: "attach" },
|
||||
{ key: "attachment-file-size-limit", env: "NTFY_ATTACHMENT_FILE_SIZE_LIMIT", section: "attach", def: "15M" },
|
||||
{ key: "attachment-total-size-limit", env: "NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT", section: "attach", def: "5G" },
|
||||
{ key: "attachment-expiry-duration", env: "NTFY_ATTACHMENT_EXPIRY_DURATION", section: "attach", def: "3h" },
|
||||
{ key: "cache-file", env: "NTFY_CACHE_FILE", section: "cache", def: "/var/cache/ntfy/cache.db" },
|
||||
{ key: "cache-file", env: "NTFY_CACHE_FILE", section: "cache" },
|
||||
{ key: "cache-duration", env: "NTFY_CACHE_DURATION", section: "cache", def: "12h" },
|
||||
{ key: "web-push-public-key", env: "NTFY_WEB_PUSH_PUBLIC_KEY", section: "webpush" },
|
||||
{ key: "web-push-private-key", env: "NTFY_WEB_PUSH_PRIVATE_KEY", section: "webpush" },
|
||||
{ key: "web-push-file", env: "NTFY_WEB_PUSH_FILE", section: "webpush", def: "/var/lib/ntfy/webpush.db" },
|
||||
{ key: "web-push-file", env: "NTFY_WEB_PUSH_FILE", section: "webpush" },
|
||||
{ key: "web-push-email-address", env: "NTFY_WEB_PUSH_EMAIL_ADDRESS", section: "webpush" },
|
||||
{ key: "smtp-sender-addr", env: "NTFY_SMTP_SENDER_ADDR", section: "smtp-out" },
|
||||
{ key: "smtp-sender-from", env: "NTFY_SMTP_SENDER_FROM", section: "smtp-out" },
|
||||
{ key: "smtp-sender-user", env: "NTFY_SMTP_SENDER_USER", section: "smtp-out" },
|
||||
{ key: "smtp-sender-pass", env: "NTFY_SMTP_SENDER_PASS", section: "smtp-out" },
|
||||
{ key: "smtp-server-listen", env: "NTFY_SMTP_SERVER_LISTEN", section: "smtp-in", def: ":25" },
|
||||
{ key: "smtp-server-listen", env: "NTFY_SMTP_SERVER_LISTEN", section: "smtp-in" },
|
||||
{ key: "smtp-server-domain", env: "NTFY_SMTP_SERVER_DOMAIN", section: "smtp-in" },
|
||||
{ key: "smtp-server-addr-prefix", env: "NTFY_SMTP_SERVER_ADDR_PREFIX", section: "smtp-in" },
|
||||
{ key: "upstream-base-url", env: "NTFY_UPSTREAM_BASE_URL", section: "upstream" },
|
||||
];
|
||||
|
||||
var DOCKER_PATH_MAP = {
|
||||
"/var/cache/ntfy/cache.db": "/var/lib/ntfy/cache.db",
|
||||
"/var/cache/ntfy/attachments": "/var/lib/ntfy/attachments",
|
||||
};
|
||||
|
||||
// Feature checkbox → nav tab ID
|
||||
var NAV_MAP = {
|
||||
"cg-feat-auth": "cg-nav-auth",
|
||||
@@ -80,6 +74,7 @@
|
||||
val = el.value.trim();
|
||||
if (!val) return;
|
||||
}
|
||||
if (val && c.def && val === c.def) return;
|
||||
if (val) values[c.key] = val;
|
||||
});
|
||||
|
||||
@@ -146,6 +141,7 @@
|
||||
upstream: "# Upstream",
|
||||
};
|
||||
var lastSection = "";
|
||||
var hadAuth = false;
|
||||
|
||||
CONFIG.forEach(function (c) {
|
||||
if (!(c.key in values)) return;
|
||||
@@ -154,6 +150,7 @@
|
||||
if (sections[c.section]) lines.push(sections[c.section]);
|
||||
lastSection = c.section;
|
||||
}
|
||||
if (c.section === "auth") hadAuth = true;
|
||||
var val = values[c.key];
|
||||
if (c.type === "bool") {
|
||||
lines.push(c.key + ": true");
|
||||
@@ -162,36 +159,54 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Find where auth section ends to insert users/acls/tokens there
|
||||
var authInsertIdx = lines.length;
|
||||
if (hadAuth) {
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i] === "# Access control") {
|
||||
// Find the end of this section (next section comment or end)
|
||||
for (var j = i + 1; j < lines.length; j++) {
|
||||
if (lines[j].indexOf("# ") === 0) { authInsertIdx = j - 1; break; }
|
||||
authInsertIdx = j + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var authExtra = [];
|
||||
if (values["_auth-users"]) {
|
||||
if (lastSection !== "auth") { lines.push(""); lines.push("# Access control"); }
|
||||
lines.push("auth-users:");
|
||||
if (!hadAuth) { authExtra.push(""); authExtra.push("# Access control"); hadAuth = true; }
|
||||
authExtra.push("auth-users:");
|
||||
values["_auth-users"].forEach(function (u) {
|
||||
lines.push(' - "' + u.username + ":" + u.password + ":" + u.role + '"');
|
||||
authExtra.push(' - "' + u.username + ":" + u.password + ":" + u.role + '"');
|
||||
});
|
||||
}
|
||||
|
||||
if (values["_auth-acls"]) {
|
||||
if (!values["_auth-users"] && lastSection !== "auth") { lines.push(""); lines.push("# Access control"); }
|
||||
lines.push("auth-access:");
|
||||
if (!hadAuth) { authExtra.push(""); authExtra.push("# Access control"); hadAuth = true; }
|
||||
authExtra.push("auth-access:");
|
||||
values["_auth-acls"].forEach(function (a) {
|
||||
lines.push(' - "' + (a.user || "*") + ":" + a.topic + ":" + a.permission + '"');
|
||||
authExtra.push(' - "' + (a.user || "*") + ":" + a.topic + ":" + a.permission + '"');
|
||||
});
|
||||
}
|
||||
|
||||
if (values["_auth-tokens"]) {
|
||||
lines.push("auth-tokens:");
|
||||
if (!hadAuth) { authExtra.push(""); authExtra.push("# Access control"); hadAuth = true; }
|
||||
authExtra.push("auth-tokens:");
|
||||
values["_auth-tokens"].forEach(function (t) {
|
||||
var entry = t.user + ":" + t.token;
|
||||
if (t.label) entry += ":" + t.label;
|
||||
lines.push(' - "' + entry + '"');
|
||||
authExtra.push(' - "' + entry + '"');
|
||||
});
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
// Splice auth extras into the right position
|
||||
if (authExtra.length) {
|
||||
lines.splice.apply(lines, [authInsertIdx, 0].concat(authExtra));
|
||||
}
|
||||
|
||||
function dockerPath(p) {
|
||||
return DOCKER_PATH_MAP[p] || p;
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function generateDockerCompose(values) {
|
||||
@@ -207,8 +222,6 @@
|
||||
var val = values[c.key];
|
||||
if (c.type === "bool") {
|
||||
val = "true";
|
||||
} else {
|
||||
val = dockerPath(val);
|
||||
}
|
||||
if (val.indexOf("$") !== -1) {
|
||||
val = val.replace(/\$/g, "$$$$");
|
||||
@@ -243,12 +256,11 @@
|
||||
}
|
||||
|
||||
lines.push(" volumes:");
|
||||
lines.push(" - ./:/var/lib/ntfy");
|
||||
lines.push(" - /var/cache/ntfy:/var/cache/ntfy");
|
||||
lines.push(" - /etc/ntfy:/etc/ntfy");
|
||||
lines.push(" ports:");
|
||||
|
||||
var listen = values["listen-http"] || ":80";
|
||||
var port = listen.replace(/.*:/, "");
|
||||
lines.push(' - "8080:' + port + '"');
|
||||
lines.push(' - "80:80"');
|
||||
lines.push(" restart: unless-stopped");
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
@@ -449,10 +461,6 @@
|
||||
if (el && !el.value.trim()) el.value = value;
|
||||
}
|
||||
|
||||
function prefillSelect(modal, key, value) {
|
||||
var el = modal.querySelector('[data-key="' + key + '"]');
|
||||
if (el) el.value = value;
|
||||
}
|
||||
|
||||
function updateVisibility() {
|
||||
var modal = document.getElementById("cg-modal");
|
||||
@@ -514,6 +522,14 @@
|
||||
var emailInSection = modal.querySelector("#cg-email-in-section");
|
||||
if (emailInSection) emailInSection.style.display = smtpInEnabled ? "" : "none";
|
||||
|
||||
// Show/hide configure buttons next to feature checkboxes
|
||||
modal.querySelectorAll(".cg-btn-configure").forEach(function (btn) {
|
||||
var row = btn.closest(".cg-feature-row");
|
||||
if (!row) return;
|
||||
var cb = row.querySelector('input[type="checkbox"]');
|
||||
btn.style.display = (cb && cb.checked) ? "" : "none";
|
||||
});
|
||||
|
||||
// If active nav tab got hidden, switch to General
|
||||
var activeNav = modal.querySelector(".cg-nav-tab.active");
|
||||
if (activeNav && activeNav.style.display === "none") {
|
||||
@@ -554,6 +570,22 @@
|
||||
proxyCheckbox.checked = proxyYes.checked;
|
||||
}
|
||||
|
||||
// Default access select → hidden input
|
||||
var accessSelect = modal.querySelector("#cg-default-access-select");
|
||||
var accessHidden = modal.querySelector('input[type="hidden"][data-key="auth-default-access"]');
|
||||
if (accessSelect && accessHidden) {
|
||||
accessHidden.value = accessSelect.value;
|
||||
}
|
||||
|
||||
// Login/signup radios → hidden checkboxes
|
||||
var loginYes = modal.querySelector('input[name="cg-enable-login"][value="yes"]');
|
||||
var loginHidden = modal.querySelector("#cg-enable-login-hidden");
|
||||
if (loginYes && loginHidden) loginHidden.checked = loginYes.checked;
|
||||
|
||||
var signupYes = modal.querySelector('input[name="cg-enable-signup"][value="yes"]');
|
||||
var signupHidden = modal.querySelector("#cg-enable-signup-hidden");
|
||||
if (signupYes && signupHidden) signupHidden.checked = signupYes.checked;
|
||||
|
||||
// --- Pre-fill defaults ---
|
||||
if (isPostgres) {
|
||||
prefill(modal, "database-url", "postgres://user:pass@host:5432/ntfy");
|
||||
@@ -563,9 +595,17 @@
|
||||
if (!isPostgres) prefill(modal, "auth-file", "/var/lib/ntfy/auth.db");
|
||||
}
|
||||
if (isPrivate) {
|
||||
prefillSelect(modal, "auth-default-access", "deny-all");
|
||||
// Set default access select to deny-all
|
||||
if (accessSelect) accessSelect.value = "deny-all";
|
||||
if (accessHidden) accessHidden.value = "deny-all";
|
||||
// Enable login
|
||||
var loginYesRadio = modal.querySelector('input[name="cg-enable-login"][value="yes"]');
|
||||
if (loginYesRadio) loginYesRadio.checked = true;
|
||||
if (loginHidden) loginHidden.checked = true;
|
||||
} else {
|
||||
prefillSelect(modal, "auth-default-access", "read-write");
|
||||
// Open server: reset default access to read-write
|
||||
if (accessSelect) accessSelect.value = "read-write";
|
||||
if (accessHidden) accessHidden.value = "read-write";
|
||||
}
|
||||
|
||||
if (cacheEnabled) {
|
||||
@@ -661,8 +701,54 @@
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
|
||||
var resetBtn = document.getElementById("cg-reset-btn");
|
||||
|
||||
function resetAll() {
|
||||
// Reset all text/password inputs
|
||||
modal.querySelectorAll('input[type="text"], input[type="password"]').forEach(function (el) {
|
||||
el.value = "";
|
||||
});
|
||||
// Uncheck all checkboxes
|
||||
modal.querySelectorAll('input[type="checkbox"]').forEach(function (el) {
|
||||
el.checked = false;
|
||||
el.disabled = false;
|
||||
});
|
||||
// Reset radio buttons to first option
|
||||
var radioGroups = {};
|
||||
modal.querySelectorAll('input[type="radio"]').forEach(function (el) {
|
||||
if (!radioGroups[el.name]) {
|
||||
radioGroups[el.name] = true;
|
||||
var first = modal.querySelector('input[type="radio"][name="' + el.name + '"]');
|
||||
if (first) first.checked = true;
|
||||
} else {
|
||||
el.checked = false;
|
||||
}
|
||||
});
|
||||
// Reset selects to first option
|
||||
modal.querySelectorAll("select").forEach(function (el) {
|
||||
el.selectedIndex = 0;
|
||||
});
|
||||
// Remove all repeatable rows
|
||||
modal.querySelectorAll(".cg-auth-user-row, .cg-auth-acl-row, .cg-auth-token-row").forEach(function (row) {
|
||||
row.remove();
|
||||
});
|
||||
// Re-prefill base-url
|
||||
var baseUrlInput = modal.querySelector('[data-key="base-url"]');
|
||||
if (baseUrlInput) {
|
||||
var host = window.location.hostname;
|
||||
if (host && host.indexOf("ntfy.sh") === -1) {
|
||||
baseUrlInput.value = "https://ntfy.example.com";
|
||||
}
|
||||
}
|
||||
// Reset to General tab
|
||||
switchPanel(modal, "cg-panel-general");
|
||||
updateVisibility();
|
||||
updateOutput();
|
||||
}
|
||||
|
||||
if (openBtn) openBtn.addEventListener("click", openModal);
|
||||
if (closeBtn) closeBtn.addEventListener("click", closeModal);
|
||||
if (resetBtn) resetBtn.addEventListener("click", resetAll);
|
||||
if (backdrop) backdrop.addEventListener("click", closeModal);
|
||||
|
||||
document.addEventListener("keydown", function (e) {
|
||||
@@ -679,6 +765,14 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Configure buttons in feature grid
|
||||
modal.querySelectorAll(".cg-btn-configure").forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
var panelId = btn.getAttribute("data-panel");
|
||||
if (panelId) switchPanel(modal, panelId);
|
||||
});
|
||||
});
|
||||
|
||||
// Output format tab switching
|
||||
modal.querySelectorAll(".cg-output-tab").forEach(function (tab) {
|
||||
tab.addEventListener("click", function () {
|
||||
|
||||
Reference in New Issue
Block a user