356 lines
21 KiB
HTML
356 lines
21 KiB
HTML
<!-- Modal subscription -->
|
||
<div class="modal fade" id="subscriptionRequiredModal" tabindex="-1" role="dialog">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">Subscription required</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>To add more domains, please setup a paid plan.</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||
<button type="button" class="btn btn-success" ng-click="openSubscriptionSetup()">Setup Subscription</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- modal domain add/configure -->
|
||
<div class="modal fade" id="domainConfigureModal" tabindex="-1" role="dialog">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h4 class="modal-title" ng-show="domainConfigure.adding">Add Domain</h4>
|
||
<h4 class="modal-title" ng-hide="domainConfigure.adding">Configure {{ domainConfigure.domain.domain }}</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form name="domainConfigureForm" role="form" novalidate ng-submit="domainConfigure.submit()" autocomplete="off">
|
||
<fieldset>
|
||
<p class="has-error text-center" ng-show="domainConfigure.error">{{ domainConfigure.error }}</p>
|
||
|
||
<div class="form-group" ng-show="domainConfigure.adding">
|
||
<label class="control-label">Domain name</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.newDomain" name="newDomain" ng-disabled="domainConfigure.busy" placeholder="example.com" ng-required="domainConfigure.adding" autofocus>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="control-label">DNS Provider <sup><a ng-href="{{ config.webServerOrigin }}/documentation/domains/#dns-providers" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
||
<select class="form-control" ng-model="domainConfigure.provider" ng-options="a.value as a.name for a in dnsProvider" ng-change="domainConfigure.setDefaultTlsProvider()"></select>
|
||
</div>
|
||
|
||
<!-- Route53 -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'route53'">
|
||
<label class="control-label">Access Key Id</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.accessKeyId" name="accessKeyId" ng-disabled="domainConfigure.busy" ng-minlength="16" ng-maxlength="32" ng-required="domainConfigure.provider === 'route53'">
|
||
</div>
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'route53'">
|
||
<label class="control-label">Secret Access Key</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.secretAccessKey" name="secretAccessKey" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'route53'">
|
||
</div>
|
||
|
||
<!-- Google Cloud DNS -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'gcdns'">
|
||
<label class="control-label">Service Account Key</label>
|
||
<div class="input-group">
|
||
<input type="file" id="gcdnsKeyFileInput" style="display:none"/>
|
||
<input type="text" class="form-control" placeholder="Service Account Key" ng-model="domainConfigure.gcdnsKey.keyFileName" id="gcdnsKeyInput" name="cert" onclick="getElementById('gcdnsKeyFileInput').click();" style="cursor: pointer;" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'gcdns'">
|
||
<span class="input-group-addon">
|
||
<i class="fa fa-upload" onclick="getElementById('gcdnsKeyFileInput').click();"></i>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DigitalOcean -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'digitalocean'">
|
||
<label class="control-label">DigitalOcean Token</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.digitalOceanToken" name="digitalOceanToken" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'digitalocean'">
|
||
</div>
|
||
|
||
<!-- Gandi -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'gandi'">
|
||
<label class="control-label">Gandi API Key</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.gandiApiKey" name="gandiApiKey" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'gandi'">
|
||
</div>
|
||
|
||
<!-- GoDaddy -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'godaddy'">
|
||
<label class="control-label">API Key</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.godaddyApiKey" name="apiKey" ng-disabled="domainConfigure.busy" ng-minlength="1" ng-required="domainConfigure.provider === 'godaddy'">
|
||
</div>
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'godaddy'">
|
||
<label class="control-label">API Secret</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.godaddyApiSecret" name="apiSecret" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'godaddy'">
|
||
</div>
|
||
|
||
<!-- Cloudflare -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'cloudflare'">
|
||
<label class="control-label">Token Type</label>
|
||
<select class="form-control" ng-model="domainConfigure.cloudflareTokenType">
|
||
<option value="GlobalApiKey">Global API Key</option>
|
||
<option value="ApiToken">API Token</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'cloudflare'">
|
||
<label class="control-label" ng-show="domainConfigure.cloudflareTokenType === 'GlobalApiKey'">Global API Key</label>
|
||
<label class="control-label" ng-show="domainConfigure.cloudflareTokenType === 'ApiToken'">Api Token</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.cloudflareToken" name="cloudflareToken" placeholder="API Key/Token" ng-required="domainConfigure.provider === 'cloudflare'" ng-disabled="domainConfigure.busy">
|
||
</div>
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'cloudflare' && domainConfigure.cloudflareTokenType === 'GlobalApiKey'">
|
||
<label class="control-label">Cloudflare Email</label>
|
||
<input type="email" class="form-control" ng-model="domainConfigure.cloudflareEmail" name="cloudflareEmail" placeholder="Cloudflare Account Email" ng-required="domainConfigure.provider === 'cloudflare' && domainConfigure.cloudflareTokenType === 'GlobalApiKey'" ng-disabled="domainConfigure.busy">
|
||
</div>
|
||
|
||
<!-- Linode -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'linode'">
|
||
<label class="control-label">Linode Token</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.linodeToken" name="linodeToken" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'linode'">
|
||
</div>
|
||
|
||
<!-- Name.com -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'namecom'">
|
||
<label class="control-label">Name.com Username</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.nameComUsername" name="nameComUsername" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'namecom'">
|
||
</div>
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'namecom'">
|
||
<label class="control-label">API Token</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.nameComToken" name="nameComToken" ng-disabled="domainConfigure.busy" ng-minlength="1" ng-required="domainConfigure.provider === 'namecom'">
|
||
</div>
|
||
|
||
<!-- Namecheap -->
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'namecheap'">
|
||
<label class="control-label">Namecheap Username</label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.namecheapUsername" name="namecheapUsername" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'namecheap'">
|
||
</div>
|
||
<div class="form-group" ng-class="{ 'has-error': false }" ng-show="domainConfigure.provider === 'namecheap'">
|
||
<label class="control-label">API Key</label>
|
||
<p class="small text-info">
|
||
<b>The server IP needs to be whitelisted for this API Key.</b>
|
||
</p>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.namecheapApiKey" name="namecheapApiKey" ng-disabled="domainConfigure.busy" ng-minlength="1" ng-required="domainConfigure.provider === 'namecheap'">
|
||
</div>
|
||
|
||
<p class="small text-warning" ng-show="domainConfigure.provider === 'linode'">
|
||
<b>Linode DNS average <a target="_blank" ng-href="{{ config.webServerOrigin }}/documentation/domains/#linode-dns">propagation time</a> is 30 minutes. Installing apps & and getting a Let's Encrypt certificate will take a while.</b>
|
||
</p>
|
||
|
||
<p class="small text-info" ng-show="domainConfigure.provider === 'wildcard'">
|
||
Setup <i>A</i> records for <b>*.{{ domainConfigure.newDomain || domainConfigure.domain.domain }}</b> and <b>{{ domainConfigure.newDomain || domainConfigure.domain.domain }}</b> to this server's IP.
|
||
</p>
|
||
|
||
<p class="small text-info" ng-show="domainConfigure.provider === 'manual'">
|
||
<b>All DNS records have to be setup manually before each app installation.</b>
|
||
</p>
|
||
|
||
<p class="small text-info" ng-show="needsPort80(domainConfigure.provider, domainConfigure.tlsConfig.provider)">Let's Encrypt requires your server to be reachable on port 80</p>
|
||
|
||
<a href="" ng-click="domainConfigure.advancedVisible = true" ng-hide="domainConfigure.advancedVisible">Advanced settings...</a>
|
||
<div uib-collapse="!domainConfigure.advancedVisible">
|
||
|
||
<div ng-show="false">
|
||
<label class="control-label">
|
||
<input type="checkbox" ng-model="domainConfigure.hyphenatedSubdomains" name="hyphenatedSubdomains" ng-disabled="domainConfigure.busy"/> Hyphenate Subdomains
|
||
</label>
|
||
<p>When enabled, apps are installed into <code><location>-<domain></code></p>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="control-label">Zone Name (Optional) <sup><a ng-href="{{ config.webServerOrigin }}/documentation/domains/#zone-name" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
||
<input type="text" class="form-control" ng-model="domainConfigure.zoneName" name="zoneName" ng-disabled="domainConfigure.busy">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="control-label">Certificate Provider <sup><a ng-href="{{ config.webServerOrigin }}/documentation/certificates/#certificate-providers" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
||
<select class="form-control" ng-model="domainConfigure.tlsConfig.provider" ng-options="a.value as a.name for a in tlsProvider"></select>
|
||
</div>
|
||
|
||
<!-- Fallback certificate -->
|
||
<div ng-show="domainConfigure.tlsConfig.provider !== 'fallback'">
|
||
<label class="control-label">Fallback Certificate (optional)</label>
|
||
<p>
|
||
Certificates are automatically obtained and renewed from <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a>. See the current rate limit <a href="https://letsencrypt.org/docs/rate-limits/" target="_blank">here</a>.
|
||
This wildcard certificate will be used should getting a Let’s Encrypt certificate fail. If not provided, an automatically generated self-signed certificate will be used as fallback.
|
||
</p>
|
||
</div>
|
||
|
||
<div ng-show="domainConfigure.tlsConfig.provider === 'fallback'">
|
||
<label class="control-label">Custom Certificate</label>
|
||
<p>
|
||
This <a ng-href="{{ config.webServerOrigin }}/documentation/certificates/#custom-certificates" target="_blank">wildcard certificate</a> will be used for all apps on this domain. If not provided, a self-signed certificate will be automatically generated.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="form-group" ng-class="{ 'has-error': (!fallbackCert.cert.$dirty && fallbackCert.error) }">
|
||
<div class="input-group">
|
||
<input type="file" id="fallbackCertFileInput" style="display:none"/>
|
||
<input type="text" class="form-control" placeholder="Certificate" ng-model="domainConfigure.fallbackCert.certificateFileName" name="cert" onclick="getElementById('fallbackCertFileInput').click();" style="cursor: pointer;" ng-disabled="domainConfigure.busy">
|
||
<span class="input-group-addon">
|
||
<i class="fa fa-upload" onclick="getElementById('fallbackCertFileInput').click();"></i>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group" ng-class="{ 'has-error': (!fallbackCert.key.$dirty && fallbackCert.error) }">
|
||
<div class="input-group">
|
||
<input type="file" id="fallbackKeyFileInput" style="display:none"/>
|
||
<input type="text" class="form-control" placeholder="Key" ng-model="domainConfigure.fallbackCert.keyFileName" id="fallbackKeyInput" name="key" onclick="getElementById('fallbackKeyFileInput').click();" style="cursor: pointer;" ng-disabled="domainConfigure.busy">
|
||
<span class="input-group-addon">
|
||
<i class="fa fa-upload" onclick="getElementById('fallbackKeyFileInput').click();"></i>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div> <!-- advanced -->
|
||
|
||
<input class="ng-hide" type="submit" ng-disabled="domainConfigureForm.$invalid || domainConfigure.busy"/>
|
||
</fieldset>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer ">
|
||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||
<button type="submit" class="btn btn-outline btn-success pull-right" ng-click="domainConfigure.submit()" ng-disabled="domainConfigureForm.$invalid || domainConfigure.busy">
|
||
<i class="fa fa-circle-notch fa-spin" ng-show="domainConfigure.busy"></i> Save
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal domain remove -->
|
||
<div class="modal fade" id="domainRemoveModal" tabindex="-1" role="dialog">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">Really remove {{ domainRemove.domain.domain }} ?</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
This will delete the domain <code>{{ domainRemove.domain.domain }}</code>.
|
||
<div>
|
||
<br/>
|
||
<span class="has-error" ng-show="domainRemove.error">{{ domainRemove.error }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||
<button type="button" class="btn btn-danger" ng-click="domainRemove.submit()" ng-disabled="domainRemove.busy"><i class="fa fa-circle-notch fa-spin" ng-show="domainRemove.busy"></i> Remove</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content">
|
||
<div class="text-left">
|
||
<h1>Domains <button class="btn btn-primary btn-outline pull-right" ng-click="domainAdd.show()"><i class="fa fa-plus"></i> Add Domain</button></h1>
|
||
</div>
|
||
|
||
<div class="card card-large">
|
||
<div class="row ng-hide" ng-show="!ready">
|
||
<div class="col-lg-12 text-center">
|
||
<h2><i class="fa fa-circle-notch fa-spin"></i></h2>
|
||
</div>
|
||
</div>
|
||
<div class="row animateMeOpacity ng-hide" ng-show="ready">
|
||
<div class="col-lg-12">
|
||
<table class="table table-hover" style="margin-top: 10px;">
|
||
<thead>
|
||
<tr>
|
||
<th>Domain</th>
|
||
<th class="text-left hidden-xs hidden-sm">Provider</th>
|
||
<th style="width: 100px" class="text-right">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr ng-repeat="domain in domains">
|
||
<td class="elide-table-cell hand" ng-click="domain.provider !== 'caas' && domainConfigure.show(domain)">
|
||
{{ domain.domain }}
|
||
</td>
|
||
<td class="text-left elide-table-cell hidden-xs hidden-sm hand" ng-click="domain.provider !== 'caas' && domainConfigure.show(domain)">
|
||
{{ prettyProviderName(domain) }}
|
||
</td>
|
||
<td class="text-right no-wrap" style="vertical-align: bottom">
|
||
<button class="btn btn-xs btn-default" ng-click="domainConfigure.show(domain)" ng-show="domain.provider !== 'caas'" title="Edit Domain"><i class="fa fa-pencil-alt"></i></button>
|
||
<button class="btn btn-xs btn-danger" ng-click="domainRemove.show(domain)" ng-show="domain.provider !== 'caas'" title="Remove Domain" ng-disabled="domain.domain === config.adminDomain"><i class="far fa-trash-alt"></i></button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-left">
|
||
<h3>Renew certificates</h3>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="row">
|
||
<div class="col-md-12">
|
||
<p>
|
||
Cloudron renews Let's Encrypt certificates automatically. Use this option to trigger a renewal immediately.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-12" style="margin-bottom: 10px;">
|
||
<div ng-show="renewCerts.busy" class="progress progress-striped active animateMe">
|
||
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ renewCerts.percent }}%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<p ng-show="renewCerts.busy">{{ renewCerts.message }}</p>
|
||
<p ng-hide="renewCerts.busy">
|
||
<div class="has-error" ng-show="!renewCerts.active">{{ renewCerts.errorMessage }}</div>
|
||
</p>
|
||
</div>
|
||
<div class="col-md-6 text-right">
|
||
<button class="btn btn-outline btn-primary" ng-click="renewCerts.renew()" ng-disabled="renewCerts.busy" style="margin-right: 10px">Renew All Certs</button>
|
||
<a class="btn btn-primary pull-right" ng-href="/logs.html?taskId={{renewCerts.taskId}}" ng-disabled="!renewCerts.taskId" target="_blank">Show Logs</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-left">
|
||
<h3>Change Dashboard Domain</h3>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<p>
|
||
This will move the dashboard to the <code>my</code>subdomain of the selected domain. Email server will be reconfigured to
|
||
send notifications from this domain.
|
||
</p>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<select class="form-control pull-right" style="display: inline-block; width: 200px;" ng-model="changeDashboard.selectedDomain" ng-options="a.domain for a in domains"></select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-12" style="margin-bottom: 10px;">
|
||
<div ng-show="changeDashboard.busy" class="progress progress-striped active animateMe">
|
||
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ changeDashboard.percent }}%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<p ng-show="changeDashboard.busy">{{ changeDashboard.message }}</p>
|
||
<p ng-hide="changeDashboard.busy">
|
||
<div class="has-error" ng-show="!changeDashboard.active">{{ changeDashboard.errorMessage }}</div>
|
||
</p>
|
||
</div>
|
||
<div class="col-md-6 text-right">
|
||
<button class="btn btn-outline btn-primary" ng-click="changeDashboard.change()" ng-hide="changeDashboard.busy" ng-disabled="changeDashboard.selectedDomain.domain === changeDashboard.adminDomain.domain">Change Domain</button>
|
||
<button class="btn btn-outline btn-danger" ng-click="changeDashboard.stop()" ng-show="changeDashboard.busy" style="margin-right: 10px">Cancel</button>
|
||
<a class="btn btn-primary pull-right" ng-href="/logs.html?taskId={{changeDashboard.taskId}}" ng-show="changeDashboard.busy" target="_blank">Show Logs</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|