Add role change form to admin settings page

This commit is contained in:
Nassim Jahnke 2023-02-14 13:19:33 +01:00
parent 586ef6e707
commit 8e75016312
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
10 changed files with 97 additions and 14 deletions

View File

@ -11,6 +11,7 @@ import io.papermc.hangar.controller.extras.pagination.filters.log.LogProjectFilt
import io.papermc.hangar.controller.extras.pagination.filters.log.LogSubjectFilter;
import io.papermc.hangar.controller.extras.pagination.filters.log.LogUserFilter;
import io.papermc.hangar.controller.extras.pagination.filters.log.LogVersionFilter;
import io.papermc.hangar.db.dao.internal.table.roles.RolesDAO;
import io.papermc.hangar.model.api.PaginatedResult;
import io.papermc.hangar.model.api.requests.RequestPagination;
import io.papermc.hangar.model.common.NamedPermission;
@ -18,10 +19,12 @@ import io.papermc.hangar.model.common.roles.GlobalRole;
import io.papermc.hangar.model.db.JobTable;
import io.papermc.hangar.model.db.UserTable;
import io.papermc.hangar.model.db.roles.GlobalRoleTable;
import io.papermc.hangar.model.db.roles.RoleTable;
import io.papermc.hangar.model.internal.admin.health.MissingFileCheck;
import io.papermc.hangar.model.internal.admin.health.UnhealthyProject;
import io.papermc.hangar.model.internal.api.requests.StringContent;
import io.papermc.hangar.model.internal.api.requests.admin.ChangePlatformVersionsForm;
import io.papermc.hangar.model.internal.api.requests.admin.ChangeRoleForm;
import io.papermc.hangar.model.internal.api.responses.HealthReport;
import io.papermc.hangar.model.internal.logs.HangarLoggedAction;
import io.papermc.hangar.security.annotations.permission.PermissionRequired;
@ -42,6 +45,7 @@ import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -65,9 +69,10 @@ public class AdminController extends HangarComponent {
private final UserService userService;
private final ObjectMapper mapper;
private final GlobalRoleService globalRoleService;
private final RolesDAO rolesDAO;
@Autowired
public AdminController(final PlatformService platformService, final StatService statService, final HealthService healthService, final JobService jobService, final UserService userService, final ObjectMapper mapper, final GlobalRoleService globalRoleService) {
public AdminController(final PlatformService platformService, final StatService statService, final HealthService healthService, final JobService jobService, final UserService userService, final ObjectMapper mapper, final GlobalRoleService globalRoleService, final RolesDAO rolesDAO) {
this.platformService = platformService;
this.statService = statService;
this.healthService = healthService;
@ -75,6 +80,7 @@ public class AdminController extends HangarComponent {
this.userService = userService;
this.mapper = mapper;
this.globalRoleService = globalRoleService;
this.rolesDAO = rolesDAO;
}
@ResponseStatus(HttpStatus.OK)
@ -84,6 +90,17 @@ public class AdminController extends HangarComponent {
this.platformService.updatePlatformVersions(form);
}
@ResponseStatus(HttpStatus.OK)
@PostMapping(path = "/roles", consumes = MediaType.APPLICATION_JSON_VALUE)
@PermissionRequired(NamedPermission.MANUAL_VALUE_CHANGES)
@Transactional
public void changeRoles(@RequestBody final List<@Valid ChangeRoleForm> roles) {
for (final ChangeRoleForm role : roles) {
System.out.println(role.roleId());
this.rolesDAO.update(role.roleId(), role.title(), role.color(), role.rank());
}
}
@ResponseBody
@PermissionRequired(NamedPermission.VIEW_STATS)
@GetMapping(path = "/stats", produces = MediaType.APPLICATION_JSON_VALUE)

View File

@ -1,6 +1,7 @@
package io.papermc.hangar.db.dao.internal.table.roles;
import io.papermc.hangar.model.db.roles.RoleTable;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jdbi.v3.sqlobject.config.RegisterConstructorMapper;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.customizer.Timestamped;
@ -16,6 +17,9 @@ public interface RolesDAO {
@SqlUpdate("INSERT INTO roles VALUES (:id, :now, :name, :category, :title, :color, :assignable, :rank, cast(:permission AS bit(64)))")
void insert(@BindBean RoleTable roleTable);
@SqlUpdate("UPDATE roles VALUES SET title = :title, color = :color, rank = :rank WHERE id = :id")
void update(long id, String title, String color, @Nullable Integer rank);
@SqlQuery("SELECT id, created_at, name, category, title, color, assignable, rank, permission::bigint FROM roles WHERE id = :id")
RoleTable getById(long id);
}

View File

@ -30,7 +30,7 @@ public class RoleTable extends Table implements Named {
this.permission = permission;
}
private RoleTable(final long id, final String name, final RoleCategory category, final String title, final String color, final boolean assignable, final Integer rank, final Permission permission) {
public RoleTable(final long id, final String name, final RoleCategory category, final String title, final String color, final boolean assignable, final Integer rank, final Permission permission) {
super(id);
this.name = name;
this.category = category;

View File

@ -0,0 +1,7 @@
package io.papermc.hangar.model.internal.api.requests.admin;
import jakarta.validation.constraints.NotBlank;
import org.checkerframework.checker.nullness.qual.Nullable;
public record ChangeRoleForm(long roleId, @NotBlank String title, @NotBlank String color, @Nullable Integer rank) {
}

View File

@ -5,11 +5,13 @@ import io.papermc.hangar.model.common.roles.GlobalRole;
import io.papermc.hangar.model.db.roles.GlobalRoleTable;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class GlobalRoleService extends RoleService<GlobalRoleTable, GlobalRole, GlobalRolesDAO> {
@Autowired
public GlobalRoleService(final GlobalRolesDAO roleDao) {
super(roleDao);
}

View File

@ -341,8 +341,8 @@ function isRecent(date: string): boolean {
<DropdownItem v-if="hasPerms(NamedPermission.VIEW_STATS)" to="/admin/stats">{{ t("nav.user.stats") }}</DropdownItem>
<DropdownItem v-if="hasPerms(NamedPermission.VIEW_HEALTH)" to="/admin/health">{{ t("nav.user.health") }}</DropdownItem>
<DropdownItem v-if="hasPerms(NamedPermission.VIEW_LOGS)" to="/admin/log">{{ t("nav.user.log") }}</DropdownItem>
<DropdownItem v-if="hasPerms(NamedPermission.MANUAL_VALUE_CHANGES)" to="/admin/versions">
{{ t("nav.user.platformVersions") }}
<DropdownItem v-if="hasPerms(NamedPermission.MANUAL_VALUE_CHANGES)" to="/admin/settings">
{{ t("nav.user.adminSettings") }}
</DropdownItem>
<DropdownItem v-if="hasPerms(NamedPermission.EDIT_ALL_USER_SETTINGS)" to="/admin/user/">
{{ t("nav.user.userList") }}

@ -1 +1 @@
Subproject commit 6cac2d63bb9d266665b4931cf99bfb6b5742c23e
Subproject commit e6e51d36f08276d341a6a8eebc44426848405128

View File

@ -91,7 +91,7 @@
"stats": "Stats",
"health": "Hangar Health",
"log": "User Action Log",
"platformVersions": "Platform Versions",
"adminSettings": "Admin settings",
"userList": "User List",
"logout": "Sign out",
"error": {

View File

@ -1,8 +1,7 @@
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { useRoute, useRouter } from "vue-router";
import { computed, Ref, ref } from "vue";
import { cloneDeep, isEqual } from "lodash-es";
import { Ref, ref } from "vue";
import { useHead } from "@vueuse/head";
import { PlatformVersion } from "hangar-internal";
import { handleRequestError } from "~/composables/useErrorHandling";
@ -17,6 +16,8 @@ import { useSeo } from "~/composables/useSeo";
import { useNotificationStore } from "~/lib/store/notification";
import { definePageMeta } from "#imports";
import { Platform } from "~/types/enums";
import InputText from "~/lib/components/ui/InputText.vue";
import { integer } from "~/lib/composables/useValidationHelpers";
definePageMeta({
globalPermsRequired: ["MANUAL_VALUE_CHANGES"],
@ -38,7 +39,7 @@ const fullVersions: Ref<Record<Platform, string[]>> = ref({
WATERFALL: [],
VELOCITY: [],
});
reset();
resetPlatformVersions();
function versions(versions: PlatformVersion[]): string[] {
const fullVersions = [];
@ -52,7 +53,7 @@ function versions(versions: PlatformVersion[]): string[] {
return fullVersions;
}
async function save() {
async function savePlatformVersions() {
loading.value = true;
const data: { [key: string]: string[] } = {};
for (const pl of platforms || []) {
@ -68,11 +69,28 @@ async function save() {
}
}
function reset() {
function resetPlatformVersions() {
for (const platform of useBackendData.platforms.values()) {
fullVersions.value[platform.enumName] = versions(platform.possibleVersions);
}
}
const roles = ref([...useBackendData.orgRoles, ...useBackendData.globalRoles, ...useBackendData.projectRoles]);
async function saveRoles() {
loading.value = true;
const data = [];
for (const role of roles.value) {
data.push({ roleId: role.roleId, title: role.title, color: role.color, rank: role.rank });
}
try {
await useInternalApi("admin/roles", "post", roles.value);
notification.success("Updated roles!");
router.go(0);
} catch (e: any) {
loading.value = false;
handleRequestError(e);
}
}
</script>
<template>
@ -99,10 +117,45 @@ function reset() {
<template #footer>
<span class="flex justify-end items-center gap-2">
Updates may take a while to take effect!
<Button @click="reset">{{ i18n.t("general.reset") }}</Button>
<Button :disabled="loading" @click="save"> {{ i18n.t("platformVersions.saveChanges") }}</Button>
<Button @click="resetPlatformVersions">{{ i18n.t("general.reset") }}</Button>
<Button :disabled="loading" @click="savePlatformVersions"> {{ i18n.t("platformVersions.saveChanges") }}</Button>
</span>
</template>
</Card>
<div class="mt-5">
<Card>
<PageTitle>Roles</PageTitle>
<Table class="w-full">
<thead>
<tr>
<th>Title</th>
<th>Category</th>
<th>Color</th>
<th>Rank</th>
</tr>
</thead>
<tbody>
<tr v-for="role in roles" :key="role.roleId">
<td>
<InputText v-model="role.title" />
</td>
<td>{{ role.roleCategory }}</td>
<td>
<InputText v-model="role.color" />
</td>
<td>
<InputText v-model="role.rank" :rules="[integer()]" />
</td>
</tr>
</tbody>
</Table>
<template #footer>
<span class="flex justify-end items-center gap-2">
<Button :disabled="loading" @click="saveRoles"> {{ i18n.t("platformVersions.saveChanges") }}</Button>
</span>
</template>
</Card>
</div>
</div>
</template>

View File

@ -7,7 +7,7 @@ declare module "hangar-api" {
rank?: number | null;
value: string;
roleId: number;
category: RoleCategory;
roleCategory: RoleCategory;
permissions: string;
title: string;
color: string;