195 lines
13 KiB
Handlebars
195 lines
13 KiB
Handlebars
<div class="page-header">
|
|
<div>
|
|
<h2>{{#if isEdit}}Edit API source - {{apiSource.name}}{{else}}Add API source{{/if}}</h2>
|
|
<p>{{#if isEdit}}Adjust the endpoint or refresh cadence and review the latest JSON response.{{else}}Adjust the endpoint or refresh cadence for this API source.{{/if}}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form id="api-source-form" method="post" action="{{formAction}}" {{{formAttrs}}}>
|
|
<div class="card card-outline card-primary admin-form-card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">API source details</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-12 col-lg-5">
|
|
<label for="api-source-name" class="form-label">Name</label>
|
|
<input id="api-source-name" name="name" class="form-control" value="{{apiSource.name}}" maxlength="128" data-limit-text-length required />
|
|
</div>
|
|
<div class="col-12 col-lg-5">
|
|
<label for="api-source-url" class="form-label">API URL</label>
|
|
<input id="api-source-url" name="api_url" type="url" class="form-control" value="{{apiSource.apiUrl}}" maxlength="1024" data-limit-text-length placeholder="https://example.com/api.json" required />
|
|
</div>
|
|
<div class="col-12 col-lg-2">
|
|
<label for="api-source-request-method" class="form-label">Request method</label>
|
|
<select id="api-source-request-method" name="request_method" class="form-select" data-api-source-request-method>
|
|
<option value="GET" {{#if (eq apiSource.requestMethod 'GET')}}selected{{/if}}>GET</option>
|
|
<option value="POST" {{#if (eq apiSource.requestMethod 'POST')}}selected{{/if}}>POST</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="mb-4" data-api-source-request-section>
|
|
<h4 class="api-source-section-heading">Request</h4>
|
|
<div class="row g-3 align-items-end">
|
|
<div class="col-12">
|
|
<div class="form-text mb-2">POST requests use JSON with the existing authentication settings.</div>
|
|
<textarea id="api-source-request-body" name="request_body_json" class="form-control font-monospace" rows="5" data-api-source-request-body placeholder="{ "ids": [1, 2, 3] }" spellcheck="false">{{apiSource.requestBodyJson}}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-4">
|
|
<h4 class="api-source-section-heading">Access and parsing</h4>
|
|
<div class="row g-3 align-items-end">
|
|
<div class="col-12 col-md-4">
|
|
<label for="api-source-auth-method" class="form-label">Auth method</label>
|
|
<select id="api-source-auth-method" name="auth_method" class="form-select" data-api-source-auth-method>
|
|
<option value="none" {{#if (eq apiSource.authMethod 'none')}}selected{{/if}}>None</option>
|
|
<option value="basic" {{#if (eq apiSource.authMethod 'basic')}}selected{{/if}}>Basic</option>
|
|
<option value="bearer" {{#if (eq apiSource.authMethod 'bearer')}}selected{{/if}}>Bearer token</option>
|
|
<option value="api_key_header" {{#if (eq apiSource.authMethod 'api_key_header')}}selected{{/if}}>API key header</option>
|
|
<option value="token_login" {{#if (eq apiSource.authMethod 'token_login')}}selected{{/if}}>Login then token</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-12 col-md-8">
|
|
<label for="api-source-items-path" class="form-label">Items path</label>
|
|
<input id="api-source-items-path" name="items_path" class="form-control" value="{{apiSource.itemsPath}}" maxlength="255" data-limit-text-length placeholder="items, results.data, or another array path" />
|
|
</div>
|
|
</div>
|
|
<div class="row mt-2">
|
|
<div class="col-12 col-md-8 offset-md-4">
|
|
<div class="form-text">Optional. Use dot notation to change the default base for placeholders.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-4" data-api-source-auth-details-section>
|
|
<h4 class="api-source-section-heading">Authentication details</h4>
|
|
<div class="row g-3 mb-3" data-api-source-auth-panel="basic" hidden>
|
|
<div class="col-12 col-md-6">
|
|
<label for="api-source-auth-username" class="form-label">Username</label>
|
|
<input id="api-source-auth-username" name="auth_username" class="form-control" value="{{apiSource.authUsername}}" maxlength="255" data-limit-text-length autocomplete="username" />
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label for="api-source-auth-password" class="form-label">Password</label>
|
|
<input id="api-source-auth-password" name="auth_password" type="password" class="form-control" value="{{apiSource.authPassword}}" maxlength="255" data-limit-text-length autocomplete="current-password" />
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-3" data-api-source-auth-panel="bearer" hidden>
|
|
<div class="col-12">
|
|
<label for="api-source-auth-bearer-token" class="form-label">Bearer token</label>
|
|
<div class="input-group">
|
|
<input id="api-source-auth-bearer-token" name="auth_bearer_token" type="password" class="form-control" value="{{apiSource.authBearerToken}}" maxlength="255" data-limit-text-length autocomplete="off" spellcheck="false" autocapitalize="off" autocorrect="off" data-api-source-bearer-token-input />
|
|
<button type="button" class="btn btn-outline-secondary" aria-label="Show bearer token" aria-pressed="false" data-api-source-bearer-token-toggle>
|
|
<i class="bi bi-eye" aria-hidden="true"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-3" data-api-source-auth-panel="api_key_header" hidden>
|
|
<div class="col-12 col-md-5">
|
|
<label for="api-source-auth-header-name" class="form-label">Header name</label>
|
|
<input id="api-source-auth-header-name" name="auth_header_name" class="form-control" value="{{apiSource.authHeaderName}}" maxlength="255" data-limit-text-length placeholder="X-API-Key" />
|
|
</div>
|
|
<div class="col-12 col-md-7">
|
|
<label for="api-source-auth-header-value" class="form-label">Header value</label>
|
|
<input id="api-source-auth-header-value" name="auth_header_value" class="form-control" value="{{apiSource.authHeaderValue}}" maxlength="255" data-limit-text-length />
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-3" data-api-source-auth-panel="token_login" hidden>
|
|
<div class="col-12 col-md-8">
|
|
<label for="api-source-token-url" class="form-label">Login / token URL</label>
|
|
<input id="api-source-token-url" name="token_url" type="url" class="form-control" value="{{apiSource.tokenUrl}}" maxlength="1024" placeholder="https://example.com/login" />
|
|
<div class="form-text">The login request is sent as POST with the JSON body below.</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<label for="api-source-token-response-path" class="form-label">Token response path</label>
|
|
<input id="api-source-token-response-path" name="token_response_path" class="form-control" value="{{apiSource.tokenResponsePath}}" placeholder="access_token" />
|
|
<div class="form-text">Example: <code>data.token</code></div>
|
|
</div>
|
|
<div class="col-12">
|
|
<label for="api-source-token-body" class="form-label">Login request body</label>
|
|
<textarea id="api-source-token-body" name="token_request_body_json" class="form-control font-monospace" rows="4" placeholder="{ "username": "...", "password": "..." }" spellcheck="false">{{apiSource.tokenRequestBodyJson}}</textarea>
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label for="api-source-token-header-name" class="form-label">API token header</label>
|
|
<input id="api-source-token-header-name" name="token_header_name" class="form-control" value="{{apiSource.tokenHeaderName}}" placeholder="Authorization" />
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label for="api-source-token-header-prefix" class="form-label">Token prefix</label>
|
|
<input id="api-source-token-header-prefix" name="token_header_prefix" class="form-control" value="{{apiSource.tokenHeaderPrefix}}" placeholder="Bearer" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4 class="api-source-section-heading">Refresh</h4>
|
|
<div class="row g-3">
|
|
<div class="col-12 col-md-4">
|
|
<label for="api-source-interval" class="form-label">Update interval</label>
|
|
<input id="api-source-interval" name="update_interval_value" type="number" min="1" class="form-control" value="{{apiSource.updateIntervalValue}}" required />
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<label for="api-source-interval-unit" class="form-label">Unit</label>
|
|
<select id="api-source-interval-unit" name="update_interval_unit" class="form-select">
|
|
<option value="seconds" {{#if (eq apiSource.updateIntervalUnit 'seconds')}}selected{{/if}}>Seconds</option>
|
|
<option value="minutes" {{#if (eq apiSource.updateIntervalUnit 'minutes')}}selected{{/if}}>Minutes</option>
|
|
<option value="hours" {{#if (eq apiSource.updateIntervalUnit 'hours')}}selected{{/if}}>Hours</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer d-flex justify-content-end align-items-center">
|
|
<div class="me-auto">{{#if isEdit}}{{#if (hasPermission currentUser "api-sources.update")}}<button type="submit" form="api-source-toggle-form" data-async-data-source-toggle data-enabled="{{#if apiSource.enabled}}true{{else}}false{{/if}}" class="btn {{#if apiSource.enabled}}btn-danger{{else}}btn-success{{/if}}"><i class="bi {{#if apiSource.enabled}}bi-pause-fill{{else}}bi-play-fill{{/if}} me-1" aria-hidden="true"></i>{{#if apiSource.enabled}}Disable{{else}}Enable{{/if}}</button>{{/if}} {{#if (hasPermission currentUser "api-sources.allow")}}<button type="button" data-manual-refresh-url="/data-sources/api-sources/{{apiSource.id}}/refresh" class="btn btn-outline-secondary"><i class="bi bi-arrow-clockwise me-1" aria-hidden="true"></i>Refresh now</button>{{/if}}{{/if}}</div>
|
|
{{{saveActionButtons formId="api-source-form" saveUrl=formAction cancelUrl=cancelUrl deleteUrl=deleteUrl deleteDisabled=deleteDisabled showSaveAndClose=showSaveSecondaryActions showSaveAndNew=showSaveSecondaryActions}}}
|
|
</div>
|
|
</div>
|
|
</form>
|
|
{{#if isEdit}}<form id="api-source-toggle-form" method="post" action="/data-sources/api-sources/{{apiSource.id}}" data-async-command><input type="hidden" name="data_source_action" value="toggle" /></form>{{/if}}
|
|
|
|
<div class="card card-outline card-secondary mt-3" id="api-source-response-panel">
|
|
<div class="card-header d-flex align-items-center">
|
|
<h3 class="card-title mb-0">Latest response</h3>
|
|
{{#if lastResponseJson}}
|
|
<div class="btn-group btn-group-sm ms-auto" role="group" aria-label="JSON tree actions">
|
|
<button type="button" class="btn btn-outline-secondary" data-json-toggle-collapse-all>
|
|
<i class="bi bi-arrows-collapse me-1"></i>
|
|
Collapse all
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" data-json-toggle-expand-all>
|
|
<i class="bi bi-arrows-expand me-1"></i>
|
|
Expand all
|
|
</button>
|
|
</div>
|
|
{{/if}}
|
|
</div>
|
|
<div class="card-body">
|
|
{{#if lastPullError}}
|
|
<div class="alert alert-warning">{{lastPullError}}</div>
|
|
{{/if}}
|
|
<dl class="row">
|
|
<dt class="col-12 col-md-3">Last pulled</dt>
|
|
<dd class="col-12 col-md-9">
|
|
{{#if apiSource.lastPulledAtValue}}
|
|
<time data-local-datetime datetime="{{apiSource.lastPulledAtValue}}">{{#if apiSource.lastPulledAtLabel}}{{apiSource.lastPulledAtLabel}}{{else}}{{apiSource.lastPulledAtValue}}{{/if}}</time>
|
|
{{else}}
|
|
-
|
|
{{/if}}
|
|
</dd>
|
|
|
|
<dt class="col-12 col-md-3">Status</dt>
|
|
<dd class="col-12 col-md-9">{{#if apiSource.lastResponseStatus}}{{apiSource.lastResponseStatus}}{{else}}-{{/if}}</dd>
|
|
|
|
<dt class="col-12 col-md-3">Content type</dt>
|
|
<dd class="col-12 col-md-9">{{#if apiSource.lastResponseContentType}}{{apiSource.lastResponseContentType}}{{else}}-{{/if}}</dd>
|
|
</dl>
|
|
{{#if lastResponseJson}}
|
|
<div data-json-toggle-panel>
|
|
<script type="application/json" data-json-toggle-source>{{json lastResponseJson}}</script>
|
|
<div class="mb-0 small bg-body-tertiary border rounded p-3 font-monospace" style="white-space: pre-wrap; word-break: break-word;" data-json-toggle-output>{{lastResponseJson}}</div>
|
|
</div>
|
|
{{else}}
|
|
<div class="alert alert-secondary mb-0">No JSON response is stored yet.</div>
|
|
{{/if}}
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/assets/js/data-sources/api-source-form.js?v={{appVersion}}"></script> |