mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 05:19:05 +01:00
feat: add edit of notice box content to admin settings
This commit is contained in:
parent
38ce40ae7d
commit
00a578657c
@ -110,6 +110,7 @@ func (api *RestApi) MountConfigApiRoutes(r *mux.Router) {
|
|||||||
r.HandleFunc("/users/", api.getUsers).Methods(http.MethodGet)
|
r.HandleFunc("/users/", api.getUsers).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/users/", api.deleteUser).Methods(http.MethodDelete)
|
r.HandleFunc("/users/", api.deleteUser).Methods(http.MethodDelete)
|
||||||
r.HandleFunc("/user/{id}", api.updateUser).Methods(http.MethodPost)
|
r.HandleFunc("/user/{id}", api.updateUser).Methods(http.MethodPost)
|
||||||
|
r.HandleFunc("/notice/", api.editNotice).Methods(http.MethodPost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1285,6 +1286,69 @@ func (api *RestApi) updateUser(rw http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editNotice godoc
|
||||||
|
// @summary Updates or empties the notice box content
|
||||||
|
// @tags User
|
||||||
|
// @description Modifies the content of notice.txt, shown as notice box on the homepage.
|
||||||
|
// @description If more than one formValue is set then only the highest priority field is used.
|
||||||
|
// @description Only accessible from IPs registered with apiAllowedIPs configuration option.
|
||||||
|
// @accept mpfd
|
||||||
|
// @produce plain
|
||||||
|
// @param new-content formData string false "Priority 1: New content to display"
|
||||||
|
// @success 200 {string} string "Success Response Message"
|
||||||
|
// @failure 400 {string} string "Bad Request"
|
||||||
|
// @failure 401 {string} string "Unauthorized"
|
||||||
|
// @failure 403 {string} string "Forbidden"
|
||||||
|
// @failure 422 {string} string "Unprocessable Entity: The user could not be updated"
|
||||||
|
// @failure 500 {string} string "Internal Server Error"
|
||||||
|
// @security ApiKeyAuth
|
||||||
|
// @router /notice/ [post]
|
||||||
|
func (api *RestApi) editNotice(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
err := securedCheck(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) {
|
||||||
|
http.Error(rw, "Only admins are allowed to update the notice.txt file", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Value
|
||||||
|
newContent := r.FormValue("new-content")
|
||||||
|
|
||||||
|
// Check FIle
|
||||||
|
noticeExists := util.CheckFileExists("./var/notice.txt")
|
||||||
|
if !noticeExists {
|
||||||
|
ntxt, err := os.Create("./var/notice.txt")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Creating ./var/notice.txt failed: %s", err.Error())
|
||||||
|
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ntxt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if newContent != "" {
|
||||||
|
if err := os.WriteFile("./var/notice.txt", []byte(newContent), 0o666); err != nil {
|
||||||
|
log.Errorf("Writing to ./var/notice.txt failed: %s", err.Error())
|
||||||
|
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
rw.Write([]byte("Update Notice Content Success"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := os.WriteFile("./var/notice.txt", []byte(""), 0o666); err != nil {
|
||||||
|
log.Errorf("Writing to ./var/notice.txt failed: %s", err.Error())
|
||||||
|
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
rw.Write([]byte("Empty Notice Content Success"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) {
|
||||||
err := securedCheck(r)
|
err := securedCheck(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,7 +35,7 @@ type Route struct {
|
|||||||
|
|
||||||
var routes []Route = []Route{
|
var routes []Route = []Route{
|
||||||
{"/", "home.tmpl", "ClusterCockpit", false, setupHomeRoute},
|
{"/", "home.tmpl", "ClusterCockpit", false, setupHomeRoute},
|
||||||
{"/config", "config.tmpl", "Settings", false, func(i InfoType, r *http.Request) InfoType { return i }},
|
{"/config", "config.tmpl", "Settings", false, setupConfigRoute},
|
||||||
{"/monitoring/jobs/", "monitoring/jobs.tmpl", "Jobs - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { return i }},
|
{"/monitoring/jobs/", "monitoring/jobs.tmpl", "Jobs - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { return i }},
|
||||||
{"/monitoring/job/{id:[0-9]+}", "monitoring/job.tmpl", "Job <ID> - ClusterCockpit", false, setupJobRoute},
|
{"/monitoring/job/{id:[0-9]+}", "monitoring/job.tmpl", "Job <ID> - ClusterCockpit", false, setupJobRoute},
|
||||||
{"/monitoring/users/", "monitoring/list.tmpl", "Users - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { i["listType"] = "USER"; return i }},
|
{"/monitoring/users/", "monitoring/list.tmpl", "Users - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { i["listType"] = "USER"; return i }},
|
||||||
@ -80,6 +80,17 @@ func setupHomeRoute(i InfoType, r *http.Request) InfoType {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupConfigRoute(i InfoType, r *http.Request) InfoType {
|
||||||
|
if util.CheckFileExists("./var/notice.txt") {
|
||||||
|
msg, err := os.ReadFile("./var/notice.txt")
|
||||||
|
if err == nil {
|
||||||
|
i["ncontent"] = string(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
func setupJobRoute(i InfoType, r *http.Request) InfoType {
|
func setupJobRoute(i InfoType, r *http.Request) InfoType {
|
||||||
i["id"] = mux.Vars(r)["id"]
|
i["id"] = mux.Vars(r)["id"]
|
||||||
if config.Keys.EmissionConstant != 0 {
|
if config.Keys.EmissionConstant != 0 {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
export let isAdmin;
|
export let isAdmin;
|
||||||
export let isApi;
|
export let isApi;
|
||||||
export let username;
|
export let username;
|
||||||
|
export let ncontent;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isAdmin == true}
|
{#if isAdmin == true}
|
||||||
@ -22,7 +23,7 @@
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle class="mb-1">Admin Options</CardTitle>
|
<CardTitle class="mb-1">Admin Options</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<AdminSettings />
|
<AdminSettings {ncontent}/>
|
||||||
</Card>
|
</Card>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -6,7 +6,8 @@ new Config({
|
|||||||
props: {
|
props: {
|
||||||
isAdmin: isAdmin,
|
isAdmin: isAdmin,
|
||||||
isApi: isApi,
|
isApi: isApi,
|
||||||
username: username
|
username: username,
|
||||||
|
ncontent: ncontent,
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig],
|
['cc-config', clusterCockpitConfig],
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
import AddUser from "./admin/AddUser.svelte";
|
import AddUser from "./admin/AddUser.svelte";
|
||||||
import ShowUsers from "./admin/ShowUsers.svelte";
|
import ShowUsers from "./admin/ShowUsers.svelte";
|
||||||
import Options from "./admin/Options.svelte";
|
import Options from "./admin/Options.svelte";
|
||||||
|
import NoticeEdit from "./admin/NoticeEdit.svelte";
|
||||||
|
|
||||||
|
export let ncontent;
|
||||||
|
|
||||||
let users = [];
|
let users = [];
|
||||||
let roles = [];
|
let roles = [];
|
||||||
@ -52,4 +55,5 @@
|
|||||||
<EditProject on:reload={getUserList} />
|
<EditProject on:reload={getUserList} />
|
||||||
</Col>
|
</Col>
|
||||||
<Options />
|
<Options />
|
||||||
|
<NoticeEdit {ncontent}/>
|
||||||
</Row>
|
</Row>
|
||||||
|
78
web/frontend/src/config/admin/NoticeEdit.svelte
Normal file
78
web/frontend/src/config/admin/NoticeEdit.svelte
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<!--
|
||||||
|
@component Admin edit notice.txt content card
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Col, Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
|
export let ncontent;
|
||||||
|
|
||||||
|
let message = { msg: "", color: "#d63384" };
|
||||||
|
let displayMessage = false;
|
||||||
|
|
||||||
|
async function handleEditNotice() {
|
||||||
|
const content = document.querySelector("#notice-content").value;
|
||||||
|
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("new-content", content);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/config/notice/`, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
let text = await res.text();
|
||||||
|
popMessage(text, "#048109");
|
||||||
|
} else {
|
||||||
|
let text = await res.text();
|
||||||
|
throw new Error("Response Code " + res.status + "-> " + text);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
popMessage(err, "#d63384");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function popMessage(response, rescolor) {
|
||||||
|
message = { msg: response, color: rescolor };
|
||||||
|
displayMessage = true;
|
||||||
|
setTimeout(function () {
|
||||||
|
displayMessage = false;
|
||||||
|
}, 3500);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Card class="h-100">
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle class="mb-3">Edit Notice Shown On Homepage</CardTitle>
|
||||||
|
<p>Empty content ("No Content.") hides notice card on homepage.</p>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="No Content."
|
||||||
|
value={ncontent}
|
||||||
|
id="notice-content"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- PreventDefault on Sveltestrap-Button more complex to achieve than just use good ol' html button -->
|
||||||
|
<!-- see: https://stackoverflow.com/questions/69630422/svelte-how-to-use-event-modifiers-in-my-own-components -->
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
id="edit-notice-button"
|
||||||
|
on:click|preventDefault={handleEditNotice}>Edit Notice</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{#if displayMessage}<b
|
||||||
|
><code style="color: {message.color};" out:fade
|
||||||
|
>Update: {message.msg}</code
|
||||||
|
></b
|
||||||
|
>{/if}
|
||||||
|
</p>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
@ -13,6 +13,7 @@
|
|||||||
const filterPresets = {{ .FilterPresets }};
|
const filterPresets = {{ .FilterPresets }};
|
||||||
const clusterCockpitConfig = {{ .Config }};
|
const clusterCockpitConfig = {{ .Config }};
|
||||||
const resampleConfig = {{ .Resampling }};
|
const resampleConfig = {{ .Resampling }};
|
||||||
|
const ncontent = {{ .Infos.ncontent }};
|
||||||
</script>
|
</script>
|
||||||
<script src='/build/config.js'></script>
|
<script src='/build/config.js'></script>
|
||||||
{{end}}
|
{{end}}
|
Loading…
Reference in New Issue
Block a user