Refactor directory structure

This commit is contained in:
Jan Eitzinger
2022-06-21 17:52:36 +02:00
parent 45359cca9d
commit 81819db436
54 changed files with 767 additions and 454 deletions

10
web/templates/404.tmpl Normal file
View File

@@ -0,0 +1,10 @@
{{template "base.tmpl" .}}
{{define "content"}}
<div class="row">
<div class="col">
<div class="alert alert-error" role="alert">
404: Not found
</div>
</div>
</div>
{{end}}

50
web/templates/base.tmpl Normal file
View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>{{.Title}}</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/uPlot.min.css'>
{{block "stylesheets" .}}
{{end}}
<script>
const header = {
"username": "{{ .User.Username }}",
"isAdmin": {{ .User.IsAdmin }},
"clusters": {{ .Clusters }},
};
</script>
</head>
<body class="site">
{{block "navigation" .}}
<header id="svelte-header"></header>
{{end}}
<main class="site-content">
<div class="container">
{{block "content" .}}
Whoops, you should not see this...
{{end}}
</div>
</main>
{{block "footer" .}}
<footer class="site-footer bg-light">
<ul class="footer-list">
<li class="footer-list-item"><a class="link-secondary fs-5" href="/imprint" title="Imprint" rel="nofollow">Imprint</a></li>
<li class="footer-list-item"><a class="link-secondary fs-5" href="/privacy" title="Privacy Policy" rel="nofollow">Privacy Policy</a></li>
</ul>
</footer>
{{end}}
{{block "javascript" .}}
<script src='/build/header.js'></script>
{{end}}
</body>
</html>

288
web/templates/config.tmpl Normal file
View File

@@ -0,0 +1,288 @@
{{define "content"}}
{{if .User.IsAdmin}}
<div class="row">
<div class="col card" style="margin: 10px;">
<form id="create-user-form" method="post" action="/api/users/" class="card-body">
<h5 class="card-title">Create User</h5>
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" aria-describedby="nameHelp"/>
<div id="nameHelp" class="form-text">Optional, can be blank.</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email" aria-describedby="emailHelp"/>
<div id="emailHelp" class="form-text">Optional, can be blank.</div>
</div>
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" aria-describedby="usernameHelp"/>
<div id="usernameHelp" class="form-text">Must be unique.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" aria-describedby="passwordHelp"/>
<div id="passwordHelp" class="form-text">Only API users are allowed to have a blank password. Users with a blank password can only authenticate via Tokens.</div>
</div>
<div class="mb-3">
<p>Role:</p>
<div>
<input type="radio" id="user" name="role" value="user" checked/>
<label for="user">User (regular user, same as if created via LDAP sync.)</label>
</div>
<div>
<input type="radio" id="api" name="role" value="api"/>
<label for="api">API</label>
</div>
<div>
<input type="radio" id="admin" name="role" value="admin"/>
<label for="admin">Admin</label>
</div>
</div>
<p>
<code class="form-result"></code>
</p>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<div class="col card" style="margin: 10px;">
<div class="card-body">
<h5 class="card-title">Special Users</h5>
<p>Not created by an LDAP sync and/or having a role other than <code>user</code></p>
<div style="width: 100%; max-height: 500px; overflow-y: scroll;">
<table class="table">
<thead>
<tr>
<th>Username</th>
<th>Name</th>
<th>Email</th>
<th>Roles</th>
<th>JWT</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="users-list">
<tr>
<td colspan="4">
<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<script>
fetch('/api/users/?via-ldap=false&not-just-user=true')
.then(res => res.json())
.then(users => {
let listElement = document.querySelector('#users-list')
listElement.innerHTML = users.map(user => `
<tr id="user-${user.username}">
<td>${user.username}</td>
<td>${user.name}</td>
<td>${user.email}</td>
<td><code>${user.roles.join(', ')}</code></td>
<td><button class="btn btn-success get-jwt">Gen. JWT</button></td>
<td><button class="btn btn-danger del-user">Delete</button></td>
</tr>
`).join('')
listElement.querySelectorAll('button.get-jwt').forEach(e => e.addEventListener('click', event => {
let row = event.target.parentElement.parentElement
let username = row.children[0].innerText
fetch(`/api/jwt/?username=${username}`)
.then(res => res.text())
.then(text => alert(text))
}))
listElement.querySelectorAll('button.del-user').forEach(e => e.addEventListener('click', event => {
let row = event.target.parentElement.parentElement
let username = row.children[0].innerText
if (confirm('Are you sure?')) {
let formData = new FormData()
formData.append('username', username)
fetch('/api/users/', { method: 'DELETE', body: formData }).then(res => {
if (res.status == 200) {
row.remove()
} else {
event.target.innerText = res.statusText
}
})
}
}))
})
</script>
</div>
</div>
</div>
<div class="row">
<div class="col card" style="margin: 10px;">
<div class="card-body">
<h5 class="card-title">Add Role to User</h5>
<div class="input-group">
<input type="text" class="form-control" placeholder="username" id="add-role-username"/>
<select class="form-select" id="add-role-select">
<option selected value="">Role...</option>
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="api">API</option>
</select>
<button class="btn btn-outline-secondary" type="button" id="add-role-button">Button</button>
</div>
<script>
document.querySelector('#add-role-button').addEventListener('click', () => {
const username = document.querySelector('#add-role-username').value, role = document.querySelector('#add-role-select').value
if (username == "" || role == "") {
alert('Please fill in a username and select a role.')
return
}
let formData = new FormData()
formData.append('username', username)
formData.append('add-role', role)
fetch(`/api/user/${username}`, { method: 'POST', body: formData }).then(res => res.text()).then(status => alert(status))
})
</script>
</div>
</div>
<div class="col card" style="margin: 10px;">
<div class="card-body">
<h5 class="card-title">Scramble Names / Presentation Mode</h5>
<input type="checkbox" id="scramble-names-checkbox"/>
<script>
const scrambleNamesCheckbox = document.querySelector('#scramble-names-checkbox')
scrambleNamesCheckbox.checked = window.localStorage.getItem("cc-scramble-names") != null
scrambleNamesCheckbox.addEventListener('change', () => {
if (scrambleNamesCheckbox.checked)
window.localStorage.setItem("cc-scramble-names", "true")
else
window.localStorage.removeItem("cc-scramble-names")
})
</script>
</div>
</div>
</div>
{{end}}
<div class="row">
<div class="col card" style="margin: 10px;">
<form id="line-width-form" method="post" action="/api/configuration/" class="card-body">
<h5 class="card-title">Line Width</h5>
<input type="hidden" name="key" value="plot_general_lineWidth"/>
<div class="mb-3">
<label for="value" class="form-label">Line Width</label>
<input type="number" class="form-control" id="value" name="value" aria-describedby="lineWidthHelp" value="{{ .Config.plot_general_lineWidth }}" min="1"/>
<div id="lineWidthHelp" class="form-text">Width of the lines in the timeseries plots.</div>
</div>
<p>
<code class="form-result"></code>
</p>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<div class="col card" style="margin: 10px;">
<form id="plots-per-row-form" method="post" action="/api/configuration/" class="card-body">
<h5 class="card-title">Plots per Row</h5>
<input type="hidden" name="key" value="plot_view_plotsPerRow"/>
<div class="mb-3">
<label for="value" class="form-label">Plots per Row</label>
<input type="number" class="form-control" id="value" name="value" aria-describedby="plotsperrowHelp" value="{{ .Config.plot_view_plotsPerRow }}" min="1"/>
<div id="plotsperrowHelp" class="form-text">How many plots to show next to each other on pages such as /monitoring/job/, /monitoring/system/...</div>
</div>
<p>
<code class="form-result"></code>
</p>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<div class="col card" style="margin: 10px;">
<form id="backgrounds-form" method="post" action="/api/configuration/" class="card-body">
<h5 class="card-title">Colored Backgrounds</h5>
<input type="hidden" name="key" value="plot_general_colorBackground"/>
<div class="mb-3">
<div>
<input type="radio" id="true" name="value" value="true" {{if .Config.plot_general_colorBackground}}checked{{else}}{{end}} />
<label for="true">Yes</label>
</div>
<div>
<input type="radio" id="false" name="value" value="false" {{if .Config.plot_general_colorBackground}}{{else}}checked{{end}} />
<label for="false">No</label>
</div>
</div>
<p>
<code class="form-result"></code>
</p>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
<div class="row">
<div class="col card" style="margin: 10px;">
<form id="colorscheme-form" method="post" action="/api/configuration/" class="card-body">
<h5 class="card-title">Colorscheme for Timeseries Plots</h5>
<input type="hidden" name="key" value="plot_general_colorscheme"/>
<style>
.color-dot {
height: 10px;
width: 10px;
border-radius: 50%;
display: inline-block;
}
</style>
<table class="table">
<script>
const colorschemes = {
'Default': ["#00bfff","#0000ff","#ff00ff","#ff0000","#ff8000","#ffff00","#80ff00"],
'Autumn': ['rgb(255,0,0)','rgb(255,11,0)','rgb(255,20,0)','rgb(255,30,0)','rgb(255,41,0)','rgb(255,50,0)','rgb(255,60,0)','rgb(255,71,0)','rgb(255,80,0)','rgb(255,90,0)','rgb(255,101,0)','rgb(255,111,0)','rgb(255,120,0)','rgb(255,131,0)','rgb(255,141,0)','rgb(255,150,0)','rgb(255,161,0)','rgb(255,171,0)','rgb(255,180,0)','rgb(255,190,0)','rgb(255,201,0)','rgb(255,210,0)','rgb(255,220,0)','rgb(255,231,0)','rgb(255,240,0)','rgb(255,250,0)'],
'Beach': ['rgb(0,252,0)','rgb(0,233,0)','rgb(0,212,0)','rgb(0,189,0)','rgb(0,169,0)','rgb(0,148,0)','rgb(0,129,4)','rgb(0,145,46)','rgb(0,162,90)','rgb(0,180,132)','rgb(29,143,136)','rgb(73,88,136)','rgb(115,32,136)','rgb(81,9,64)','rgb(124,51,23)','rgb(162,90,0)','rgb(194,132,0)','rgb(220,171,0)','rgb(231,213,0)','rgb(0,0,13)','rgb(0,0,55)','rgb(0,0,92)','rgb(0,0,127)','rgb(0,0,159)','rgb(0,0,196)','rgb(0,0,233)'],
'BlueRed': ['rgb(0,0,131)','rgb(0,0,168)','rgb(0,0,208)','rgb(0,0,247)','rgb(0,27,255)','rgb(0,67,255)','rgb(0,108,255)','rgb(0,148,255)','rgb(0,187,255)','rgb(0,227,255)','rgb(8,255,247)','rgb(48,255,208)','rgb(87,255,168)','rgb(127,255,127)','rgb(168,255,87)','rgb(208,255,48)','rgb(247,255,8)','rgb(255,224,0)','rgb(255,183,0)','rgb(255,143,0)','rgb(255,104,0)','rgb(255,64,0)','rgb(255,23,0)','rgb(238,0,0)','rgb(194,0,0)','rgb(150,0,0)'],
'Rainbow': ['rgb(125,0,255)','rgb(85,0,255)','rgb(39,0,255)','rgb(0,6,255)','rgb(0,51,255)','rgb(0,97,255)','rgb(0,141,255)','rgb(0,187,255)','rgb(0,231,255)','rgb(0,255,233)','rgb(0,255,189)','rgb(0,255,143)','rgb(0,255,99)','rgb(0,255,53)','rgb(0,255,9)','rgb(37,255,0)','rgb(83,255,0)','rgb(127,255,0)','rgb(173,255,0)','rgb(217,255,0)','rgb(255,248,0)','rgb(255,203,0)','rgb(255,159,0)','rgb(255,113,0)','rgb(255,69,0)','rgb(255,23,0)'],
'Binary': ['rgb(215,215,215)','rgb(206,206,206)','rgb(196,196,196)','rgb(185,185,185)','rgb(176,176,176)','rgb(166,166,166)','rgb(155,155,155)','rgb(145,145,145)','rgb(136,136,136)','rgb(125,125,125)','rgb(115,115,115)','rgb(106,106,106)','rgb(95,95,95)','rgb(85,85,85)','rgb(76,76,76)','rgb(66,66,66)','rgb(55,55,55)','rgb(46,46,46)','rgb(36,36,36)','rgb(25,25,25)','rgb(16,16,16)','rgb(6,6,6)'],
'GistEarth': ['rgb(0,0,0)','rgb(2,7,117)','rgb(9,30,118)','rgb(16,53,120)','rgb(23,73,122)','rgb(31,93,124)','rgb(39,110,125)','rgb(47,126,127)','rgb(51,133,119)','rgb(57,138,106)','rgb(62,145,94)','rgb(66,150,82)','rgb(74,157,71)','rgb(97,162,77)','rgb(121,168,83)','rgb(136,173,85)','rgb(153,176,88)','rgb(170,180,92)','rgb(185,182,94)','rgb(189,173,99)','rgb(192,164,101)','rgb(203,169,124)','rgb(215,178,149)','rgb(226,192,176)','rgb(238,212,204)','rgb(248,236,236)'],
'BlueWaves': ['rgb(83,0,215)','rgb(43,6,108)','rgb(9,16,16)','rgb(8,32,25)','rgb(0,50,8)','rgb(27,64,66)','rgb(69,67,178)','rgb(115,62,210)','rgb(155,50,104)','rgb(178,43,41)','rgb(180,51,34)','rgb(161,78,87)','rgb(124,117,187)','rgb(78,155,203)','rgb(34,178,85)','rgb(4,176,2)','rgb(9,152,27)','rgb(4,118,2)','rgb(34,92,85)','rgb(78,92,203)','rgb(124,127,187)','rgb(161,187,87)','rgb(180,248,34)','rgb(178,220,41)','rgb(155,217,104)','rgb(115,254,210)'],
'BlueGreenRedYellow': ['rgb(0,0,0)','rgb(0,0,20)','rgb(0,0,41)','rgb(0,0,62)','rgb(0,25,83)','rgb(0,57,101)','rgb(0,87,101)','rgb(0,118,101)','rgb(0,150,101)','rgb(0,150,69)','rgb(0,148,37)','rgb(0,141,6)','rgb(60,120,0)','rgb(131,87,0)','rgb(180,25,0)','rgb(203,13,0)','rgb(208,36,0)','rgb(213,60,0)','rgb(219,83,0)','rgb(224,106,0)','rgb(229,129,0)','rgb(233,152,0)','rgb(238,176,0)','rgb(243,199,0)','rgb(248,222,0)','rgb(254,245,0)']
};
for (const name in colorschemes) {
const colorscheme = colorschemes[name]
const json = JSON.stringify(colorscheme)
const checked = json == `{{ .Config.plot_general_colorscheme }}`
document.write(
`<tr>
<th scope="col">${name}</th>
<td>
<input type="radio" name="value" value='${json}' ${checked ? 'checked' : ''}/>
</td>
<td>${colorscheme.map(color => `<span class="color-dot" style="background-color: ${color};"></span>`).join('')}</td>
</tr>`)
}
</script>
</table>
<p>
<code class="form-result"></code>
</p>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
<script>
const handleSubmit = (formSel) => {
let form = document.querySelector(formSel)
form.addEventListener('submit', event => {
event.preventDefault()
let formData = new FormData(form)
fetch(form.action, { method: 'POST', body: formData })
.then(res => res.text())
.then(text => form.querySelector('.form-result').innerText = text)
.catch(err => form.querySelector('.form-result').innerText = err.toString())
return false
})
}
handleSubmit('#create-user-form')
handleSubmit('#line-width-form')
handleSubmit('#plots-per-row-form')
handleSubmit('#backgrounds-form')
handleSubmit('#colorscheme-form')
</script>
{{end}}

44
web/templates/home.tmpl Normal file
View File

@@ -0,0 +1,44 @@
{{define "content"}}
<div class="row">
<div class="col">
<h2>Clusters</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Running Jobs (short ones not listed)</th>
<th>Total Jobs</th>
<th>Short Jobs in past 24h</th>
{{if .User.IsAdmin}}
<th>System View</th>
<th>Analysis View</th>
{{end}}
</tr>
</thead>
<tbody>
{{if .User.IsAdmin}}
{{range .Infos.clusters}}
<tr>
<td>{{.Name}}</td>
<td><a href="/monitoring/jobs/?cluster={{.Name}}&state=running">{{.RunningJobs}} jobs</a></td>
<td><a href="/monitoring/jobs/?cluster={{.Name}}">{{.TotalJobs}} jobs</a></td>
<td>{{.RecentShortJobs}}</td>
<td><a href="/monitoring/systems/{{.Name}}">System View</a></td>
<td><a href="/monitoring/analysis/{{.Name}}">Analysis View</a></td>
</tr>
{{end}}
{{else}}
{{range .Infos.clusters}}
<tr>
<td>{{.Name}}</td>
<td><a href="/monitoring/jobs/?cluster={{.Name}}&state=running">{{.RunningJobs}} jobs</a></td>
<td><a href="/monitoring/jobs/?cluster={{.Name}}">{{.TotalJobs}} jobs</a></td>
<td>{{.RecentShortJobs}}</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
</div>
{{end}}

215
web/templates/imprint.tmpl Normal file
View File

@@ -0,0 +1,215 @@
{{define "navigation"}}
<header>
<nav class="navbar navbar-expand-lg navbar-light fixed-top bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">
{{block "brand" .}}
<img style="height: 30px;" alt="ClusterCockpit Logo" src="/img/logo.png" class="d-inline-block align-top">
{{end}}
</a>
</div>
</nav>
</header>
{{end}}
{{define "content"}}
<div class="container" style="margin-top:80px;margin-bottom:80px;">
<div class="row legal">
<div class="col-xs-12">
<h1>Imprint</h1>
<h2>Website owner</h2>
<p>name</p>
<address>
address
</address>
<ul>
<li>
Phone: +49-</li>
<li>
Fax: +49-</li>
<li>
E-Mail:
</li>
</ul>
<h3>Represented by</h3>
<p>
Your institution
</p>
<p>
Notice
</p>
<h3>Website Contact (Webmaster)</h3>
<p>name</p>
<ul>
<li>
Phone: +49-
</li>
<li>
E-Mail:
</li>
</ul>
<h3>Responsible controlling authority</h3>
<address>
address
</address>
<p>
Website: </p>
<h3>ID numbers</h3>
<table class="table">
<tbody>
<tr>
<th scope="row">VAT identification number</th>
<td>xxx</td>
</tr>
<tr>
<th scope="row">Tax number</th>
<td>xxx</td>
</tr>
<tr>
<th scope="row">DUNS number</th>
<td>xxx</td>
</tr>
<tr>
<th scope="row">EORI number</th>
<td>xxx</td>
</tr>
<tr>
<th scope="row">Bank details:</th>
<td>xxx
<ul>
<li>SWIFT/BIC-Code: </li>
<li>IBAN: </li>
</ul>
</td>
</tr>
</tbody>
</table>
<h2>Reporting misuse of computers and network resources</h2>
<p>
Should you become aware of any kind of misuse of computers or network resources at XXX,
<a href="URL">please inform the department responsible for computer security</a> immediately.
<h2>Copyright</h2>
<p>
Unless specified otherwise, all pages of the INSTITUTION
website are protected by copyright. This applies in
particular to texts, images, charts, and files
containing style, sound, video, or animation data; it
also includes the design of the website.
</p>
<p>
Reproduction or use of (parts) of the web pages in
electronic or printed form and their publication
(including on the Internet) is subject to prior
approval.
</p>
<p>
Press releases, publications, scripts and information
on lectures may be reprinted and evaluated provided
that the source is indicated. Furthermore, images,
charts, and files containing text or other information,
may, in part or in their entirety, be subject to the
copyright of third parties.
</p>
<p>
All registered brands and trademarks mentioned on this
website and possibly protected by third parties are
subject without restriction to the respective
applicable trademark law and the property rights of
their respective owners. The mere mention of trademarks
on this website does not indicate that they are not
protected by third party rights.
</p>
<h2>Exclusion of liability</h2>
<p>
This imprint only belongs to the following website:<br>
<strong><em>DOMAIN</em></strong><br> The website owner
is not responsible for other websites, that are not
listet above.
</p>
<p>
The operator has compiled and verified all information
provided with great care. However, we cannot assume
liability or furnish a guarantee that the data is
correct, complete or up-to-date, or regarding the
quality or constant availability of the information
provided.
</p>
<p>
We will not be liable for any damages arising from
computer viruses or the installation and use of
software when accessing or downloading data from this
website.
</p>
<p>
Websites credited to an author reflect the opinions and insights of that author.
</p>
<p>
The operator expressly reserves the right to change,
amend or delete individual web pages, services or the
entire website without prior notice or to interrupt or
terminate the publication.
</p>
<h2>Links and references (disclaimer)</h2>
<p>
The operator is only responsible for the original
content provided in accordance with the applicable
laws. This original content is to be distinguished from
links to the websites of other operators. Through these
references marked as external links, the
operator enables visitors to access third-party
content.
</p>
<p>
The operator is not responsible for this third-party
content as the operator does not initiate the data
transmission, does not choose the recipient of the
information and does not select or have any influence
on the information transmitted. The methods used to
provide access and link to this third-party information
also do not involve any automatic short-term storage,
resulting in a full exclusion of any liability for
third-party content on the operators part.
</p>
<p>
When links to these websites were first incorporated,
however, the authors of the relevant websites or the
operators editors reviewed the external content to
ascertain whether it would possibly entail liability
under civil or criminal law. Should the operator become
aware or be made aware by others that the content of a
website linked from this site could constitute a civil
or criminal law violation, then the link will be
immediately removed as long as this is technically
feasible and within reasonable expectation.
</p>
<p>
Liability for illegal, inaccurate or incomplete content
and for damages resulting from the use or non-use of
information provided by third parties shall lie
exclusively with the respective operators of the
linked sites.
</p>
</div>
</div>
</div>
{{end}}

54
web/templates/login.tmpl Normal file
View File

@@ -0,0 +1,54 @@
{{define "navigation"}}
<header>
<nav class="navbar navbar-expand-lg navbar-light fixed-top bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">
{{block "brand" .}}
<img style="height: 30px;" alt="ClusterCockpit Logo" src="/img/logo.png" class="d-inline-block align-top">
{{end}}
</a>
</div>
</nav>
</header>
{{end}}
{{define "content"}}
<section class="content-section">
<div class="container">
<div class="row">
<div class="col-4 mx-auto">
{{if .Error}}
<div class="alert alert-warning" role="alert">
{{.Error}}
</div>
{{end}}
{{if .Info}}
<div class="alert alert-success" role="alert">
{{.Info}}
</div>
{{end}}
<div class="card">
<div class="card-header">
<h3>Login</h3>
</div>
<div class="card-body">
<form action="/login" method="post">
<div class="mb-3">
<label class="form-label" for="username">Username</label>
<input class="form-control" type="text" id="username" name="username" required autofocus/>
</div>
<div class="mb-3">
<label class="form-label" for="password">Password</label>
<input class="form-control" type="password" id="password" name="password" required/>
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
{{end}}

View File

@@ -0,0 +1,15 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/analysis.css'>
{{end}}
{{define "javascript"}}
<script>
const cluster = {{ .Infos.cluster }};
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/analysis.js'></script>
{{end}}

View File

@@ -0,0 +1,18 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/job.css'>
{{end}}
{{define "javascript"}}
<script>
const jobInfos = {
id: "{{ .Infos.id }}",
jobId: "{{ .Infos.jobId }}",
clusterId: "{{ .Infos.clusterId }}"
};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/job.js'></script>
{{end}}

View File

@@ -0,0 +1,15 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/jobs.css'>
{{end}}
{{define "javascript"}}
<script>
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/jobs.js'></script>
{{end}}

View File

@@ -0,0 +1,15 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/list.css'>
{{end}}
{{define "javascript"}}
<script>
const listType = {{ .Infos.listType }};
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/list.js'></script>
{{end}}

View File

@@ -0,0 +1,14 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/node.css'>
{{end}}
{{define "javascript"}}
<script>
const infos = {{ .Infos }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/node.js'></script>
{{end}}

View File

@@ -0,0 +1,14 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/status.css'>
{{end}}
{{define "javascript"}}
<script>
const infos = {{ .Infos }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/status.js'></script>
{{end}}

View File

@@ -0,0 +1,14 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/systems.css'>
{{end}}
{{define "javascript"}}
<script>
const infos = {{ .Infos }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/systems.js'></script>
{{end}}

View File

@@ -0,0 +1,17 @@
{{define "content"}}
<div class="container">
<div class="row justify-content-center">
<div class="col-10">
{{ range $tagType, $tagList := .Infos.tagmap }}
<div class="my-3 p-2 bg-secondary text-white text-capitalize">
{{ $tagType }}
</div>
{{ range $tagList }}
<a class="btn btn-lg btn-warning" href="/monitoring/jobs/?tag={{ .id }}" role="button">
{{ .name }} <span class="badge bg-light text-dark">{{ .count }}</span> </a>
{{end}}
{{end}}
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,15 @@
{{define "content"}}
<div id="svelte-app"></div>
{{end}}
{{define "stylesheets"}}
<link rel='stylesheet' href='/build/user.css'>
{{end}}
{{define "javascript"}}
<script>
const userInfos = {{ .Infos }};
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/user.js'></script>
{{end}}

332
web/templates/privacy.tmpl Normal file
View File

@@ -0,0 +1,332 @@
{{define "navigation"}}
<header>
<nav class="navbar navbar-expand-lg navbar-light fixed-top bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">
{{block "brand" .}}
<img style="height: 30px;" alt="ClusterCockpit Logo" src="/img/logo.png" class="d-inline-block align-top">
{{end}}
</a>
</div>
</nav>
</header>
{{end}}
{{define "content"}}
<div class="container" style="margin-top:80px;margin-bottom:80px;">
<div class="row legal">
<div class="col-md">
<h1>Privacy</h1>
<p>
INSTITUION is
responsible for its websites within the meaning of the
General Data Protection Regulation (GDPR) and other
national data protection laws as well as other data
protection regulations. It is legally represented by
its XX. For contact details, please consult the
YYY.
</p>
<p>
The respective INSTITUION institutions are responsible for
any content they make available on the websites of
INSTITUION. For questions related to specific content,
please contact the person responsible as named
LINK of this web page.
</p>
<h2>Name and address of the Data Protection Officer</h2>
<address>
address
</address>
<ul>
<li>Telefon: </span> +49 </li>
<li>Fax:</span> +49 </li>
<li>E-Mail:</li>
</ul>
<h2>General information on data processing</h2>
<h3>Scope of processing of personal data</h3>
<p>
We only process our users personal data to the extent
necessary to provide services, content and a functional
website. As a rule, personal data are only processed after
the user gives their consent. An exception applies in those
cases where it is impractical to obtain the users
prior consent and the processing of such data is
permitted by law.
</p>
<h3>Legal basis for the processing of personal data</h3>
<p>
Art. 6 (1) (a) of the EU General Data Protection Regulation
(GDPR) forms the legal basis for us to obtain the
consent of a data subject for their personal data to be
processed.<br> When processing personal data required for
the performance of a contract in which the contractual
party is the data subject, Art. 6 (1) (b) GDPR forms the
legal basis. This also applies if data has to be processed
in order to carry out pre-contractual activities.<br> Art.
6 (1) (c) GDPR forms the legal basis if personal data has
to be processed in order to fulfil a legal obligation on
the part of our organisation.<br> Art. 6 (1) (d) GDPR forms
the legal basis in the case that vital interests of the
data subject or another natural person make the processing
of personal data necessary.<br> If data processing is
necessary in order to protect the legitimate interests of
our organisation or of a third party and if the interests,
basic rights and fundamental freedoms of the data subject
do not outweigh the interests mentioned above, Art. 6 (1)
(f) GDPR forms the legal basis for such data processing.
</p>
<h3>Deletion of data and storage period</h3>
<p>
The personal data of the data subject are deleted or
blocked as soon as the reason for storing them ceases
to exist. Storage beyond this time period may occur if
provided for by European or national legislators in
directives under Union legislation, laws or other
regulations to which the data controller is subject.
Such data are also blocked or deleted if a storage period
prescribed by one of the above-named rules expires,
unless further storage of the data is necessary for
entering into or performing a contract.
</p>
<h2>Provision of the website and generation of log files</h2>
<h3>Description and scope of data processing</h3>
<p>
Each time our website is accessed, our system
automatically collects data and information from
the users computer system.<br> In this context, the
following data are collected:
</p>
<ul>
<li>
Address (URL) of the website from which the file was requested
</li>
<li>
Name of the retrieved file
</li>
<li>
Date and time of the request
</li>
<li>
Data volume transmitted
</li>
<li>
Access status (file transferred, file not found, etc.)
</li>
<li>
Description of the type of web browser and/or operating system used
</li>
<li>
Anonymised IP address of the requesting computer
</li>
</ul>
<p>
The data stored are required exclusively for technical or
statistical purposes; no comparison with other data or
disclosure to third parties occurs, not even in part.
The data are stored in our systems log files. This is not
the case for the users IP addresses or other data
that make it possible to assign the data to a specific
user: before data are stored, each dataset is anonymised by
changing the IP address. These data are not stored
together with other personal data .
</p>
<h3>Legal basis for data processing</h3>
<p>
The legal basis for the temporary storage of data and
logfiles is §§ 14, 15 TMG, § 100 Abs. 1 TKG and Art. 4
BayDSG following the tasks of Art. 11 BayEGovG and Art. 7
and 34 BayHO
</p>
<h3>Purpose of data processing</h3>
<p>
The temporary storage of the IP address by the system is
necessary in order to deliver the website to the
users computer. For this purpose, the users IP address
must remain stored for the duration of the session.<br> The
storage of such data in log files takes place in order to
ensure the websites functionality. These data also serve
to help us optimise the website and ensure that our IT
systems are secure. They are not evaluated for marketing
purposes in this respect.
</p>
<h3>Storage period</h3>
<p>
Data are deleted as soon as they are no longer necessary
for fulfilling the purpose for which they were
collected. If data have been collected for the purpose of
providing the website, they are deleted at the end of the
respective session.<br> If data are stored in log files,
they are deleted at the latest after seven days. A longer
storage period is possible. In this case, the users IP
addresses are deleted or masked so that they can no longer
be assigned to the client accessing the website.
</p>
<h3>Options for filing an objection or requesting removal</h3>
<p>
The collection of data for the purpose of providing the
website and the storage of such data in log files is
essential to the websites operation. As a consequence,
the user has no possibility to object.
</p>
<h2>Use of cookies</h2>
<h3>Description and scope of data processing</h3>
<p>
Our website uses cookies. Cookies are text files that are
saved in the users web browser or by the web browser
on the users computer system. When a user accesses a
website, a cookie can be stored in the users operating
system. This cookie contains a character string that allows
the unique identification of the browser when the
website is accessed again.
</p>
<p>
We use cookies to make our website more user-friendly. Some
parts of our website require that the requesting browser
can also be identified after changing pages.<br> During
this process, the following data are stored in the cookies
and transmitted:
</p>
<ul>
<li>
Log-in information (only in the case of protected information that is made available exclusively to FAU members)
</li>
</ul>
<p>
Technical measures are taken to pseudonymise user data
collected in this way. This means that the data can no
longer be assigned to the user. The data are not stored
together with other personal data of the user.<br> When
accessing our website, a banner informs users that cookies
are used for analysis purposes and makes reference to this
data protection policy. In connection with this, users are
also instructed how they can block the storage of cookies
in their browser settings.
</p>
<h3>Legal basis for data processing</h3>
<p>
The legal basis for the temporary storage of data and
logfiles is §§ 14, 15 TMG, § 100 Abs. 1 TKG and Art. 4
BayDSG following the tasks of Art. 11 BayEGovG and
Art. 7 and 34 BayHO
</p>
<h3>Purpose of data processing</h3>
<p>
Analysis cookies are used for the purpose of improving the
quality of our website and its content. We learn
through the analysis cookies how the website is used and in
this way can continuously optimise our web presence.
</p>
<h3>Storage period, options for filing an objection or requesting removal</h3>
<p>
As cookies are stored on the users computer and are
transmitted from it to our website, users have full
control over the use of cookies. You can deactivate or
restrict the transmission of cookies by changing the
settings in your web browser. Cookies that are already
stored can be deleted at any time. This can also be done
automatically. If cookies are deactivated for our
website, it may be the case that not all of the websites
functions can be used in full.
</p>
<h2>SSL encryption</h2>
<p>
Our website uses SSL encryption for security reasons and to
protect the transmission of confidential information,
for example enquiries you send to us as operators of
the website. You can recognise an encrypted connection
when the browsers address line changes from
<code>http://</code> to <code>https://</code> and a padlock
appears in your web browser.
</p>
<p>
If SSL encryption is activated, the data you transmit to us
cannot be read by third parties.
</p>
<h2>Rights of the data subject</h2>
<p>
With regard to the processing of your personal
data, you as a data subject are entitled to the
following rights pursuant to Art. 15 et seq. GDPR:
</p>
<ul>
<li>
You can request information as to whether we process
your personal data. If this is the case, you have the
right to information about this personal data as well
as further information in connection with the
processing (Art. 15 GDPR). Please note that this
right of access may be restricted or excluded in
certain cases (cf. in particular Art. 10 BayDSG).
</li>
<li>
In the event that personal data about you is (no
longer) accurate or incomplete, you may request that
this data be corrected and, if necessary, completed
(Art. 16 GDPR).
</li>
<li>
If the legal requirements are met, you can demand that
your personal data be erased (Art. 17 GDPR) or that
the processing of this data be restricted (Art. 18
DSGVO). However, the right to erasure pursuant to
Art. 17 (1) and (2) GDPR does not apply, inter alia,
if the processing of personal data is necessary for the
performance of a task carried out in the public
interest or in the exercise of official authority
or in the exercise of official authority vested (Art.
17 para. 3 letter b GDPR).
</li>
<li>
If you have given your consent to the processing, you
have the right to withdrawal it at any time. The
withdrawal will only take effect in the future;
this means that the withdrawal does not affect the
lawfulness of the processing operations carried out
on the basis of the consent up to the withdrawal.
</li>
<li>
For reasons arising from your particular situation, you
may also object to the processing of your personal
data by us at any time (Art. 21 GDPR). If the legal
requirements are met, we will subsequently no longer
process your personal data.
</li>
<li>
Insofar as you have consented to the processing of your
personal data or have agreed to the performance of
the contract and the data processing is carried out
automated, you may be entitled to data portability
(Art. 20 GDPR).
</li>
<li>
You have the right to lodge a complaint to a
supervisory authority within the meaning of Art. 51
GDPR about the processing of your personal data. The
responsible supervisory authority for XXX
authorities is YYY.
</li>
</ul>
</div>
</div>
</div>
{{end}}