Update vaadin

WIP: User management
This commit is contained in:
grimsi
2024-09-13 14:28:40 +02:00
parent 0fe91a1980
commit d2f720a6ed
10 changed files with 96 additions and 10 deletions
+3
View File
@@ -1,9 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="GameyfinApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<option name="ACTIVE_PROFILES" value="dev" />
<option name="ALTERNATIVE_JRE_PATH" value="BUNDLED" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<module name="gameyfin.main" />
<option name="SHORTEN_COMMAND_LINE" value="ARGS_FILE" />
<option name="SPRING_BOOT_MAIN_CLASS" value="de.grimsi.gameyfin.GameyfinApplication" />
<option name="VM_PARAMETERS" value="-XX:+AllowEnhancedClassRedefinition -XX:HotswapAgent=fatjar" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="de.grimsi.gameyfin.*" />
+2 -2
View File
@@ -18,7 +18,7 @@ group = "de.grimsi"
version = "2.0.0-SNAPSHOT"
description = "gameyfin"
java.sourceCompatibility = JavaVersion.VERSION_22
java.sourceCompatibility = JavaVersion.VERSION_21
configurations {
compileOnly {
@@ -76,7 +76,7 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile> {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_22)
jvmTarget.set(JvmTarget.JVM_21)
progressiveMode.set(true)
freeCompilerArgs.add("-Xjsr305=strict")
}
@@ -1,24 +1,39 @@
import React from "react";
import React, {useEffect, useState} from "react";
import ConfigFormField from "Frontend/components/administration/ConfigFormField";
import withConfigPage from "Frontend/components/administration/withConfigPage";
import Section from "Frontend/components/general/Section";
import * as Yup from "yup";
import {UserEndpoint} from "Frontend/generated/endpoints";
import UserInfoDto from "Frontend/generated/de/grimsi/gameyfin/users/dto/UserInfoDto";
import {UserCard} from "Frontend/components/general/UserCard";
function UserManagementLayout({getConfig, formik}: any) {
const [users, setUsers] = useState<UserInfoDto[]>([]);
useEffect(() => {
UserEndpoint.getAllUsers().then(
(response) => setUsers(response as UserInfoDto[])
);
}, []);
return (
<div className="flex flex-col flex-grow">
<Section title="Users"/>
{/* TODO */}
<Section title="Sign-Ups"/>
<div className="flex flex-row">
<ConfigFormField configElement={getConfig("users.sign-ups.allow")}/>
<ConfigFormField configElement={getConfig("users.sign-ups.confirm")}
isDisabled={!formik.values.users["sign-ups"].allow}/>
</div>
<Section title="Users"/>
<div className="grid grid-flow-col grid-cols-4 gap-4">
{users.map((user) => <UserCard user={user} key={user.username}/>)}
</div>
</div>
);
)
;
}
const validationSchema = Yup.object({
@@ -0,0 +1,21 @@
import UserInfoDto from "Frontend/generated/de/grimsi/gameyfin/users/dto/UserInfoDto";
import {Avatar, Card, Chip} from "@nextui-org/react";
import {roleToColor, roleToRoleName} from "Frontend/util/utils";
export function UserCard({user}: { user: UserInfoDto }) {
return (
<Card className="flex flex-row flex-grow items-center gap-4 p-2">
<Avatar classNames={{
base: "gradient-primary size-20",
icon: "text-background/80"
}}></Avatar>
<div className="flex flex-col gap-1">
<p className="font-semibold">{user.username}</p>
<p className="text-sm">{user.email}</p>
{user.roles?.map((role) =>
<Chip key={role} size="sm" radius="sm"
className={`text-xs bg-${roleToColor(role!)}-500`}>{roleToRoleName(role!)}</Chip>)}
</div>
</Card>
)
}
+18
View File
@@ -17,4 +17,22 @@ export function rand(min: number, max: number) {
const minCeiled = Math.ceil(min);
const maxFloored = Math.floor(max);
return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);
}
export function roleToRoleName(role: string) {
role = role.replace("ROLE_", "").toLowerCase();
return role.charAt(0).toUpperCase() + role.slice(1);
}
export function roleToColor(role: string) {
switch (role) {
case "ROLE_SUPERADMIN":
return "red";
case "ROLE_ADMIN":
return "orange";
case "ROLE_USER":
return "blue";
default:
return "gray";
}
}
@@ -3,6 +3,7 @@ package de.grimsi.gameyfin.config
import de.grimsi.gameyfin.config.dto.ConfigEntryDto
import de.grimsi.gameyfin.config.entities.ConfigEntry
import de.grimsi.gameyfin.config.persistence.ConfigRepository
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.transaction.Transactional
import org.springframework.stereotype.Service
import java.io.Serializable
@@ -12,6 +13,7 @@ import java.io.Serializable
class ConfigService(
private val appConfigRepository: ConfigRepository
) {
private val log = KotlinLogging.logger {}
/**
* Get all known config values.
@@ -20,6 +22,9 @@ class ConfigService(
* @return A map of all config values
*/
fun getAllConfigValues(prefix: String?): List<ConfigEntryDto> {
log.info { "Getting all config values for prefix '$prefix'" }
var configProperties = ConfigProperties::class.sealedSubclasses.flatMap { subclass ->
subclass.objectInstance?.let { listOf(it) } ?: listOf()
}
@@ -49,6 +54,9 @@ class ConfigService(
* @throws IllegalArgumentException if no value is set and no default value exists
*/
fun <T : Serializable> getConfigValue(configProperty: ConfigProperties<T>): T {
log.info { "Getting config value '${configProperty.key}'" }
val appConfig = appConfigRepository.findById(configProperty.key).orElse(null)
return if (appConfig != null) {
getValue(appConfig.value, configProperty)
@@ -66,6 +74,9 @@ class ConfigService(
* @throws IllegalArgumentException if no value is set and no default value exists
*/
fun getConfigValue(key: String): String {
log.info { "Getting config value '$key'" }
val configProperty = findConfigProperty(key)
val appConfig = appConfigRepository.findById(configProperty.key).orElse(null)
@@ -86,6 +97,9 @@ class ConfigService(
* @throws IllegalArgumentException if the value can't be cast to the type defined for the config property
*/
fun <T : Serializable> setConfigValue(key: String, value: T) {
log.info { "Set config value '$key' to '$value'" }
val configKey = findConfigProperty(key)
// Check if the value can be cast to the type defined for the config property
@@ -109,6 +123,9 @@ class ConfigService(
* @param key: Key of the config property
*/
fun resetConfigValue(key: String) {
log.info { "Reset config value '$key'" }
val configKey = findConfigProperty(key)
if (configKey.default == null) {
@@ -129,6 +146,9 @@ class ConfigService(
* @param key: Key of the config property
*/
fun deleteConfig(key: String) {
log.info { "Delete config value '$key'" }
val configKey = findConfigProperty(key)
appConfigRepository.deleteById(configKey.key)
}
@@ -1,6 +1,5 @@
package de.grimsi.gameyfin.setup
package de.grimsi.gameyfin.meta
import de.grimsi.gameyfin.meta.Roles
import de.grimsi.gameyfin.users.UserService
import de.grimsi.gameyfin.users.entities.Role
import de.grimsi.gameyfin.users.entities.User
@@ -22,6 +22,12 @@ class UserEndpoint(
return userService.getUserInfo(auth.name)
}
@PermitAll
@RolesAllowed(Roles.Names.SUPERADMIN, Roles.Names.ADMIN)
fun getAllUsers(): List<UserInfoDto> {
return userService.getAllUsers()
}
@PermitAll
fun registerUser(registration: UserRegistrationDto): UserInfoDto {
val user: User = registerUser(registration, listOf(Roles.USER))
@@ -35,7 +41,7 @@ class UserEndpoint(
}
@RolesAllowed(Roles.Names.SUPERADMIN, Roles.Names.ADMIN)
fun updateUser(username: String, updates: UserUpdateDto) {
fun updateUserByName(username: String, updates: UserUpdateDto) {
userService.updateUser(username, updates)
}
@@ -41,6 +41,10 @@ class UserService(
fun existsByUsername(username: String): Boolean = userRepository.findByUsername(username) != null
fun getAllUsers(): List<UserInfoDto> {
return userRepository.findAll().map { u -> toUserInfo(u) }
}
fun getUserInfo(username: String): UserInfoDto {
val user = userByUsername(username)
return toUserInfo(user)