mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 17:10:10 +00:00
Harvey Tindall
92332206f0
If enabled, jfa-go pings buildrone (hosted at builds.hrfee.pw) every 30 min for new updates. If there is one, it gets information (and if applicable, a binary) from the appropriate source (buildrone, github, or dockerhub) and displays it on the admin page. You can switch update channels between stable and unstable. For binary releases, updates are downloaded automatically and installed when the user presses update. Since this obviously introduces some "phone-home" functionality into jfa-go, I just want to say IPs are not and will not be logged by buildrone, although I may later introduce functionality to give a rough idea of the number of users (again, no IPs stored). The whole thing can also be turned off in settings.
444 lines
27 KiB
HTML
444 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" class="{{ .cssClass }}">
|
|
<head>
|
|
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/bundle.css">
|
|
<script>
|
|
window.URLBase = "{{ .urlBase }}";
|
|
window.notificationsEnabled = {{ .notifications }};
|
|
window.emailEnabled = {{ .email_enabled }};
|
|
window.ombiEnabled = {{ .ombiEnabled }};
|
|
window.usernameEnabled = {{ .username }};
|
|
window.langFile = JSON.parse({{ .language }});
|
|
window.language = "{{ .langName }}";
|
|
</script>
|
|
{{ template "header.html" . }}
|
|
<title>Admin - jfa-go</title>
|
|
</head>
|
|
<body class="max-w-full overflow-x-hidden section">
|
|
<div id="modal-login" class="modal">
|
|
<form class="modal-content card" id="form-login" href="">
|
|
<span class="heading">{{ .strings.login }}</span>
|
|
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="login-user">
|
|
<input type="password" class="field input ~neutral !high mb-1" placeholder="{{ .strings.password }}" id="login-password">
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.login }}</span>
|
|
</label>
|
|
</form>
|
|
</div>
|
|
<div id="modal-add-user" class="modal">
|
|
<form class="modal-content card" id="form-add-user" href="">
|
|
<span class="heading">{{ .strings.newUser }} <span class="modal-close">×</span></span>
|
|
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="add-user-user">
|
|
<input type="email" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}">
|
|
<input type="password" class="field input ~neutral !high mb-1" placeholder="{{ .strings.password }}" id="add-user-password">
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.create }}</span>
|
|
</label>
|
|
</form>
|
|
</div>
|
|
<div id="modal-about" class="modal">
|
|
<div class="modal-content content card">
|
|
<span class="heading">{{ .strings.aboutProgram }} <span class="modal-close">×</span></span>
|
|
<img src="{{ .urlBase }}/banner.svg" class="mt-1" alt="jfa-go banner">
|
|
<p><i class="icon ri-github-fill"></i><a href="https://github.com/hrfee/jfa-go">jfa-go</a></p>
|
|
<p>{{ .strings.version }} <span class="code monospace">{{ .version }}</span></p>
|
|
<p>{{ .strings.commitNoun }} <span class="code monospace">{{ .commit }}</span></p>
|
|
<p><a href="https://github.com/hrfee/jfa-go/blob/main/LICENSE">Available under the MIT License.</a></p>
|
|
<pre class="monospace">{{ .license }}</pre>
|
|
</div>
|
|
</div>
|
|
<div id="modal-modify-user" class="modal">
|
|
<form class="modal-content card" id="form-modify-user" href="">
|
|
<span class="heading"><span id="header-modify-user"></span> <span class="modal-close">×</span></span>
|
|
<p class="content">{{ .strings.modifySettingsDescription }}</p>
|
|
<div class="flex-row mb-1">
|
|
<label class="flex-row-group mr-1">
|
|
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-profile" checked>
|
|
<span class="button ~neutral !high supra full-width center">{{ .strings.profile }}</span>
|
|
</label>
|
|
<label class="flex-row-group ml-1">
|
|
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-user">
|
|
<span class="button ~neutral !normal supra full-width center">{{ .strings.user }}</span>
|
|
</label>
|
|
</div>
|
|
<div class="select ~neutral !normal mb-1">
|
|
<select id="modify-user-profiles"></select>
|
|
</div>
|
|
<div class="select ~neutral !normal mb-1 unfocused">
|
|
<select id="modify-user-users"></select>
|
|
</div>
|
|
<label class="switch mb-1">
|
|
<input type="checkbox" id="modify-user-homescreen" checked>
|
|
<span>{{ .strings.applyHomescreenLayout }}</span>
|
|
</label>
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.apply }}</span>
|
|
</label>
|
|
</form>
|
|
</div>
|
|
<div id="modal-delete-user" class="modal">
|
|
<form class="modal-content card" id="form-delete-user" href="">
|
|
<span class="heading"><span id="header-delete-user"></span> <span class="modal-close">×</span></span>
|
|
<div class="content mt-half">
|
|
<label class="switch mb-1">
|
|
<input type="checkbox" id="delete-user-notify" checked>
|
|
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
|
|
</label>
|
|
<textarea id="textarea-delete-user" class="textarea full-width ~neutral !normal mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~critical !normal full-width center supra submit">{{ .strings.delete }}</span>
|
|
</label>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div id="modal-extend-expiry" class="modal">
|
|
<form class="modal-content card" id="form-extend-expiry" href="">
|
|
<span class="heading"><span id="header-extend-expiry"></span> <span class="modal-close">×</span></span>
|
|
<div class="content mt-half">
|
|
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="extend-expiry-days">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="extend-expiry-hours">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="extend-expiry-minutes">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~critical !normal full-width center supra submit">{{ .strings.submit }}</span>
|
|
</label>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div id="modal-announce" class="modal">
|
|
<form class="modal-content card" id="form-announce" href="">
|
|
<span class="heading"><span id="header-announce"></span> <span class="modal-close">×</span></span>
|
|
<div class="content mt-half">
|
|
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
|
|
<input type="text" id="announce-subject" class="input ~neutral !normal mb-1 mt-half">
|
|
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
|
|
<textarea id="textarea-announce" class="textarea full-width ~neutral !normal mt-half monospace"></textarea>
|
|
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
|
|
</label>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div id="modal-customize" class="modal">
|
|
<div class="modal-content card">
|
|
<span class="heading">{{ .strings.customizeEmails }} <span class="modal-close">×</span></span>
|
|
<p class="content">{{ .strings.customizeEmailsDescription }}</p>
|
|
<div class="table-responsive">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ .strings.name }}</th>
|
|
<th>{{ .strings.reset }}</th>
|
|
<th>{{ .strings.edit }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="customize-list"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="modal-editor" class="modal">
|
|
<form class="modal-content wide card" id="form-editor" href="">
|
|
<span class="heading"><span id="header-editor"></span> <span class="modal-close">×</span></span>
|
|
<div class="row">
|
|
<div class="col flex-col content mt-half">
|
|
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
|
|
<div id="editor-variables"></div>
|
|
<label class="label supra" for="textarea-editor">{{ .strings.message }}</label>
|
|
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral !normal mt-half monospace"></textarea>
|
|
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
|
|
<div class="flex-row">
|
|
<label class="full-width ml-half">
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col card ~neutral !low">
|
|
<span class="subheading supra">{{ .strings.preview }}</span>
|
|
<div class="mt-half" id="editor-preview"></div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div id="modal-restart" class="modal">
|
|
<div class="modal-content card ~critical !low">
|
|
<span class="heading">{{ .strings.settingsRestartRequired }} <span class="modal-close">×</span></span>
|
|
<p class="content pb-1">{{ .strings.settingsRestartRequiredDescription }}</p>
|
|
<div class="fr">
|
|
<span class="button ~info !normal mb-half" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
|
|
<span class="button ~critical !normal" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="modal-refresh" class="modal">
|
|
<div class="modal-content card ~neutral !normal">
|
|
<span class="heading">{{ .strings.settingsApplied }}</span>
|
|
<p class="content">{{ .strings.settingsRefreshPage }}</p>
|
|
</div>
|
|
</div>
|
|
<div id="modal-ombi-defaults" class="modal">
|
|
<form class="modal-content card" id="form-ombi-defaults" href="">
|
|
<span class="heading">{{ .strings.ombiUserDefaults }} <span class="modal-close">×</span></span>
|
|
<p class="content">{{ .strings.ombiUserDefaultsDescription }}</p>
|
|
<div class="select ~neutral !normal mb-1">
|
|
<select></select>
|
|
</div>
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
|
|
</label>
|
|
</form>
|
|
</div>
|
|
<div id="modal-user-profiles" class="modal">
|
|
<div class="modal-content wide card">
|
|
<span class="heading">{{ .strings.userProfiles }} <span class="modal-close">×</span></span>
|
|
<p class="support lg">{{ .strings.userProfilesDescription }}</p>
|
|
<div class="table-responsive">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ .strings.name }}</th>
|
|
<th>{{ .strings.userProfilesIsDefault }}</th>
|
|
<th>{{ .strings.from }}</th>
|
|
<th>{{ .strings.userProfilesLibraries }}</th>
|
|
<th><span class="button ~neutral !high" id="button-profile-create">{{ .strings.create }}</span></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="table-profiles"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="modal-add-profile" class="modal">
|
|
<form class="modal-content card" id="form-add-profile" href="">
|
|
<span class="heading">{{ .strings.addProfile }} <span class="modal-close">×</span></span>
|
|
<p class="content">{{ .strings.addProfileDescription }}</p>
|
|
<label>
|
|
<span class="supra">{{ .strings.addProfileNameOf }} </span>
|
|
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.name }}" id="add-profile-name">
|
|
<label>
|
|
<span class="supra">{{ .strings.user }}</span>
|
|
<div class="select ~neutral !normal mt-half mb-1">
|
|
<select id="add-profile-user"></select>
|
|
</div>
|
|
</label>
|
|
<label class="switch mb-1">
|
|
<input type="checkbox" id="add-profile-homescreen" checked>
|
|
<span>{{ .strings.addProfileStoreHomescreenLayout }}</span>
|
|
</label>
|
|
<label>
|
|
<input type="submit" class="unfocused">
|
|
<span class="button ~urge !normal full-width center supra submit">{{ .strings.create }}</span>
|
|
</label>
|
|
</form>
|
|
</div>
|
|
<div id="modal-update" class="modal">
|
|
<div class="modal-content wide card">
|
|
<span class="heading">{{ .strings.updates }} <span class="modal-close">×</span></span>
|
|
<p class="content">
|
|
<h2>
|
|
<a id="update-version"></a> (<span class="monospace" id="update-commit"></span>)
|
|
</h2>
|
|
<p class="content" id="update-description"></p>
|
|
<p class="support" id="update-date"></p>
|
|
<div class="content markdown-box" id="update-changelog"></div>
|
|
</p>
|
|
<span class="button ~info !normal full-width center" id="update-download">{{ .strings.download }}</span>
|
|
<span class="button ~urge !normal full-width center" id="update-update">{{ .strings.update }}</span>
|
|
</div>
|
|
</div>
|
|
<div id="notification-box"></div>
|
|
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
|
<span class="button ~urge dropdown-button">
|
|
<i class="ri-global-line"></i>
|
|
<span class="ml-1 chev"></span>
|
|
</span>
|
|
<div class="dropdown-display">
|
|
<div class="card ~neutral !low" id="lang-list">
|
|
</div>
|
|
</div>
|
|
</span>
|
|
<div class="page-container">
|
|
<div class="mb-1">
|
|
<header class="flex flex-wrap items-center justify-between">
|
|
<div class="text-neutral-700">
|
|
<span id="button-tab-invites" class="tab-button portal">{{ .strings.invites }}</span>
|
|
<span id="button-tab-accounts" class="tab-button portal">{{ .strings.accounts }}</span>
|
|
<span id="button-tab-settings" class="tab-button portal">{{ .strings.settings }}</span>
|
|
</div>
|
|
</header>
|
|
</div>
|
|
<div class="mb-1">
|
|
<div class="text-neutral-700">
|
|
<span class="button ~critical !normal mb-1 unfocused" id="logout-button">{{ .strings.logout }}</span>
|
|
<span id="button-theme" class="button ~neutral !normal mb-1">{{ .strings.theme }}</span>
|
|
</div>
|
|
</div>
|
|
<div id="tab-invites">
|
|
<div class="card ~neutral !low invites mb-1">
|
|
<span class="heading">{{ .strings.invites }}</span>
|
|
<div id="invites"></div>
|
|
</div>
|
|
<div class="card ~neutral !low">
|
|
<span class="heading">{{ .strings.create }}</span>
|
|
<div class="row" id="create-inv">
|
|
<div class="card ~neutral !normal col">
|
|
<div class="flex-row mb-1">
|
|
<label class="flex-row-group mr-1">
|
|
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
|
|
<span class="button ~neutral !high supra full-width center">{{ .strings.inviteDuration }}</span>
|
|
</label>
|
|
<label class="flex-row-group ml-1">
|
|
<input type="radio" name="duration" class="unfocused" id="radio-user-expiry">
|
|
<span class="button ~neutral !normal supra full-width center">{{ .strings.userExpiry }}</span>
|
|
</label>
|
|
</div>
|
|
<div id="inv-duration">
|
|
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="create-days">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="create-hours">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="create-minutes">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div id="user-expiry" class="unfocused">
|
|
<p class="support">{{ .strings.userExpiryDescription }}</p>
|
|
<div class="mb-half">
|
|
<label for="create-user-expiry-enabled" class="button ~neutral !normal">
|
|
<input type="checkbox" id="create-user-expiry-enabled" aria-label="User duration enabled">
|
|
<span class="ml-half">{{ .strings.enabled }} </span>
|
|
</label>
|
|
</div>
|
|
<label class="label supra" for="user-days">{{ .strings.inviteDays }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="user-days">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label class="label supra" for="user-hours">{{ .strings.inviteHours }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="user-hours">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
<label class="label supra" for="user-minutes">{{ .strings.inviteMinutes }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="user-minutes">
|
|
<option>0</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
|
|
<input type="text" id="create-label" class="input ~neutral !normal mb-1 mt-half">
|
|
</div>
|
|
<div class="card ~neutral !normal col">
|
|
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
|
|
<div class="flex-expand mb-1 mt-half">
|
|
<input type="number" min="0" id="create-uses" class="input ~neutral !normal mr-1" value=1>
|
|
<label for="create-inf-uses" class="button ~neutral !normal" title="Set uses to infinite">
|
|
<span>∞</span>
|
|
<input type="checkbox" class="unfocused" id="create-inf-uses" aria-label="Set uses to infinite">
|
|
</label>
|
|
</div>
|
|
<p class="support unfocused" id="create-inf-uses-warning"><span class="badge ~critical">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
|
|
<label class="label supra">{{ .strings.profile }}</label>
|
|
<div class="select ~neutral !normal mb-1 mt-half">
|
|
<select id="create-profile">
|
|
</select>
|
|
</div>
|
|
<div id="create-send-to-container">
|
|
<label class="label supra">{{ .strings.inviteSendToEmail }}</label>
|
|
<div class="flex-expand mb-1 mt-half">
|
|
<input type="email" id="create-send-to" class="input ~neutral !normal mr-1" placeholder="example@example.com">
|
|
<label for="create-send-to-enabled" class="button ~neutral !normal">
|
|
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<span class="button ~urge !normal supra full-width center lg" id="create-submit">{{ .strings.create }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="tab-accounts" class="unfocused">
|
|
<div class="card ~neutral !low accounts mb-1">
|
|
<span class="heading">{{ .strings.accounts }}</span>
|
|
<div class="fr">
|
|
<span class="button ~neutral !normal mb-half" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
|
|
<span class="button ~info !normal mb-half" id="accounts-announce">{{ .strings.announce }}</span>
|
|
<span class="button ~urge !normal mb-half" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
|
|
<span class="button ~warning !normal mb-half" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
|
|
<span class="button ~critical !normal mb-half" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
|
|
</div>
|
|
<div class="card ~neutral !normal accounts-header table-responsive mt-half">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th><input type="checkbox" value="" id="accounts-select-all"></th>
|
|
<th>{{ .strings.username }}</th>
|
|
<th>{{ .strings.emailAddress }}</th>
|
|
<th>{{ .strings.expiry }}</th>
|
|
<th>{{ .strings.lastActiveTime }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="accounts-list"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="tab-settings" class="unfocused">
|
|
<div class="card ~neutral !low settings overflow">
|
|
<span class="heading">{{ .strings.settings }}</span>
|
|
<div class="fr">
|
|
<span class="button ~neutral !normal" id="settings-restart">{{ .strings.settingsRestart }}</span>
|
|
<span class="button ~urge !normal unfocused" id="settings-save">{{ .strings.settingsSave }}</span>
|
|
</div>
|
|
<div class="row">
|
|
<div class="card ~neutral !normal col" id="settings-sidebar">
|
|
<aside class="aside sm ~info mb-half" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info">R</span> indicates changes require a restart.</aside>
|
|
<span class="button ~neutral !low settings-section-button mb-half" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-half"></i></span></span>
|
|
<span class="button ~neutral !low settings-section-button mb-half" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-half"></i></span></span>
|
|
</div>
|
|
<div class="card ~neutral !normal col overflow" id="settings-panel"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script src="{{ .urlBase }}/js/admin.js" type="module"></script>
|
|
</body>
|
|
</html>
|