mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Update vaadin
WIP: User management
This commit is contained in:
@@ -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
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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
-2
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user