mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Refine error handling in UI
Implement SystemEndpoint
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Generate Hilla sources" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="hillaGenerate" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
+18
-11
@@ -1,12 +1,13 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("org.springframework.boot") version "3.2.2"
|
||||
id("io.spring.dependency-management") version "1.1.4"
|
||||
val kotlinVersion = "2.0.0"
|
||||
id("org.springframework.boot") version "3.3.0"
|
||||
id("io.spring.dependency-management") version "1.1.5"
|
||||
id("dev.hilla") version "2.5.6"
|
||||
kotlin("jvm") version "1.9.22"
|
||||
kotlin("plugin.spring") version "1.9.22"
|
||||
kotlin("plugin.jpa") version "1.9.22"
|
||||
kotlin("jvm") version kotlinVersion
|
||||
kotlin("plugin.spring") version kotlinVersion
|
||||
kotlin("plugin.jpa") version kotlinVersion
|
||||
java
|
||||
}
|
||||
|
||||
@@ -18,7 +19,7 @@ group = "de.grimsi"
|
||||
version = "2.0.0-SNAPSHOT"
|
||||
description = "gameyfin"
|
||||
|
||||
java.sourceCompatibility = JavaVersion.VERSION_21
|
||||
java.sourceCompatibility = JavaVersion.VERSION_22
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
@@ -34,6 +35,7 @@ repositories {
|
||||
}
|
||||
|
||||
extra["hillaVersion"] = "2.5.6"
|
||||
val springCloudVersion by extra("2023.0.2")
|
||||
|
||||
dependencies {
|
||||
// Spring Boot & Kotlin
|
||||
@@ -54,6 +56,7 @@ dependencies {
|
||||
// Persistence
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("com.github.paulcwarren:spring-content-fs-boot-starter:3.0.7")
|
||||
implementation("org.springframework.cloud:spring-cloud-starter")
|
||||
|
||||
// Development
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
@@ -67,13 +70,17 @@ dependencies {
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom("dev.hilla:hilla-bom:${property("hillaVersion")}")
|
||||
mavenBom("org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += "-Xjsr305=strict"
|
||||
jvmTarget = "21"
|
||||
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)
|
||||
progressiveMode.set(true)
|
||||
freeCompilerArgs.add("-Xjsr305=strict")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ import {themeNames} from "Frontend/theming/themes";
|
||||
import {AuthProvider} from "Frontend/util/auth";
|
||||
import {IconContext} from "@phosphor-icons/react";
|
||||
import {Toaster} from "Frontend/@/components/ui/sonner";
|
||||
import client from "Frontend/generated/connect-client.default";
|
||||
import {ErrorHandlingMiddleware} from "Frontend/util/middleware";
|
||||
|
||||
export default function App() {
|
||||
const navigate = useNavigate();
|
||||
client.middlewares.push(ErrorHandlingMiddleware);
|
||||
|
||||
return (
|
||||
<NextUIProvider className="size-full" navigate={navigate}>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import {Middleware, MiddlewareContext, MiddlewareNext} from '@hilla/frontend';
|
||||
import {toast} from "sonner";
|
||||
import {getReasonPhrase} from "http-status-codes";
|
||||
|
||||
export const ErrorHandlingMiddleware: Middleware = async function(
|
||||
context: MiddlewareContext,
|
||||
next: MiddlewareNext
|
||||
) {
|
||||
const {endpoint, method} = context;
|
||||
|
||||
let response: Response = await next(context);
|
||||
if(!response.ok) {
|
||||
//Ignore calls to UserEndpoint.getUserInfo since they are managed by Hilla and called on initial load
|
||||
if(endpoint == "UserEndpoint" && method == "getUserInfo") return response;
|
||||
|
||||
toast.error(`${getReasonPhrase(response.status)}`, {description: `${endpoint}.${method}`})
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ function SetupView() {
|
||||
password: values.password,
|
||||
email: values.email
|
||||
});
|
||||
toast("Setup finished", {description: "Have fun with Gameyfin!"});
|
||||
toast.success("Setup finished", {description: "Have fun with Gameyfin!"});
|
||||
navigate('/login');
|
||||
} catch (e) {
|
||||
alert("An error occurred while completing the setup. Please try again.")
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button} from "@nextui-org/react";
|
||||
import {toast} from "sonner";
|
||||
import {SystemEndpoint} from "Frontend/generated/endpoints";
|
||||
|
||||
export default function TestView() {
|
||||
return (
|
||||
<div className="flex grow justify-center mt-12">
|
||||
<div className="grow justify-center mt-12">
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<Link to="/setup">Setup</Link>
|
||||
<Button onPress={
|
||||
() => toast("Setup finished", {
|
||||
description: "Have fun with Gameyfin!",
|
||||
action: {
|
||||
label: "OK",
|
||||
onClick: () => console.log("Ok"),
|
||||
}
|
||||
})}>Toast</Button>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Button onPress={
|
||||
() => toast("Normal", {
|
||||
description: "Description",
|
||||
action: {
|
||||
label: "OK",
|
||||
onClick: () => {},
|
||||
}
|
||||
})}>Toast (Normal)</Button>
|
||||
<Button onPress={
|
||||
() => toast.success("Success", {
|
||||
description: "Description",
|
||||
action: {
|
||||
label: "OK",
|
||||
onClick: () => {},
|
||||
}
|
||||
})}>Toast (Success)</Button>
|
||||
<Button onPress={
|
||||
() => toast.error("Error", {
|
||||
description: "Description",
|
||||
action: {
|
||||
label: "OK",
|
||||
onClick: () => {},
|
||||
}
|
||||
})}>Toast (Error)</Button>
|
||||
</div>
|
||||
<Button onPress={() => SystemEndpoint.restart()}>Restart</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Generated
+7
@@ -36,6 +36,7 @@
|
||||
"construct-style-sheets-polyfill": "3.1.0",
|
||||
"formik": "^2.4.5",
|
||||
"framer-motion": "^11.1.7",
|
||||
"http-status-codes": "^2.3.0",
|
||||
"lit": "3.1.0",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.2.0",
|
||||
@@ -9493,6 +9494,12 @@
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/http-status-codes": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz",
|
||||
"integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/idb": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
|
||||
|
||||
+4
-2
@@ -31,6 +31,7 @@
|
||||
"construct-style-sheets-polyfill": "3.1.0",
|
||||
"formik": "^2.4.5",
|
||||
"framer-motion": "^11.1.7",
|
||||
"http-status-codes": "^2.3.0",
|
||||
"lit": "3.1.0",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.2.0",
|
||||
@@ -102,7 +103,8 @@
|
||||
"@nextui-org/react": "$@nextui-org/react",
|
||||
"framer-motion": "$framer-motion",
|
||||
"@material-tailwind/react": "$@material-tailwind/react",
|
||||
"sonner": "$sonner"
|
||||
"sonner": "$sonner",
|
||||
"http-status-codes": "$http-status-codes"
|
||||
},
|
||||
"vaadin": {
|
||||
"dependencies": {
|
||||
@@ -146,6 +148,6 @@
|
||||
"workbox-core": "7.0.0",
|
||||
"workbox-precaching": "7.0.0"
|
||||
},
|
||||
"hash": "3305a1ae01d771a26115b08f2597b5fbb020e6535692fd453407cba700f727ea"
|
||||
"hash": "e078b3ecf381b7be4b804c8e2cd928faa9accb3412cfb55cfb649f9633cd1d41"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
package de.grimsi.gameyfin.config
|
||||
|
||||
enum class Roles(val roleName: String) {
|
||||
SUPERADMIN("ROLE_SUPERADMIN"),
|
||||
ADMIN("ROLE_ADMIN"),
|
||||
USER("ROLE_USER")
|
||||
SUPERADMIN(Names.SUPERADMIN),
|
||||
ADMIN(Names.ADMIN),
|
||||
USER(Names.USER);
|
||||
|
||||
// necessary for the ability to use the Roles class in the @RolesAllowed annotation
|
||||
class Names {
|
||||
companion object {
|
||||
const val SUPERADMIN = "ROLE_SUPERADMIN"
|
||||
const val ADMIN = "ROLE_ADMIN"
|
||||
const val USER = "ROLE_USER"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,16 @@ class SetupDataLoader(
|
||||
log.info { "We will now set up some data..." }
|
||||
|
||||
setupRoles()
|
||||
//setupUsers()
|
||||
setupUsers()
|
||||
|
||||
log.info { "Setup completed..." }
|
||||
log.info { "Visit http://${InetAddress.getLocalHost().hostName}:${env.getProperty("server.port")}/setup to complete the setup" }
|
||||
}
|
||||
|
||||
fun setupUsers() {
|
||||
|
||||
log.info { "Setting up users..." }
|
||||
|
||||
val superadmin = User(
|
||||
username = "admin",
|
||||
password = "admin"
|
||||
@@ -49,6 +52,8 @@ class SetupDataLoader(
|
||||
)
|
||||
|
||||
userService.registerUser(user, Roles.USER)
|
||||
|
||||
log.info { "User setup completed." }
|
||||
}
|
||||
|
||||
fun setupRoles() {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.grimsi.gameyfin.system
|
||||
|
||||
import de.grimsi.gameyfin.config.Roles
|
||||
import dev.hilla.Endpoint
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
|
||||
@Endpoint
|
||||
class SystemEndpoint(
|
||||
private val systemService: SystemService
|
||||
) {
|
||||
|
||||
@RolesAllowed(Roles.Names.SUPERADMIN, Roles.Names.ADMIN)
|
||||
fun restart() {
|
||||
systemService.restart()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.grimsi.gameyfin.system
|
||||
|
||||
import org.springframework.cloud.context.restart.RestartEndpoint
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class SystemService(
|
||||
private val restartEndpoint: RestartEndpoint,
|
||||
) {
|
||||
fun restart() {
|
||||
restartEndpoint.restart()
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,7 @@ import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
@Endpoint
|
||||
class UserEndpoint(
|
||||
private val userService: UserService,
|
||||
private val roleService: RoleService,
|
||||
private val userService: UserService
|
||||
) {
|
||||
|
||||
@PermitAll
|
||||
|
||||
@@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotNull
|
||||
@Entity
|
||||
class Role(
|
||||
@NotNull
|
||||
@Column(unique = true)
|
||||
var rolename: String,
|
||||
|
||||
@Id
|
||||
|
||||
@@ -8,23 +8,27 @@ import jakarta.validation.constraints.NotNull
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
class User(
|
||||
@field:NotNull
|
||||
@NotNull
|
||||
@Column(unique = true)
|
||||
var username: String,
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
var id: Long? = null,
|
||||
|
||||
@field:NotNull
|
||||
@NotNull
|
||||
var password: String,
|
||||
|
||||
@field:Nullable
|
||||
@Nullable
|
||||
@Column(unique = true)
|
||||
var email: String? = null,
|
||||
|
||||
var email_confirmed: Boolean = false,
|
||||
|
||||
var enabled: Boolean = true,
|
||||
|
||||
@Embedded
|
||||
@field:Nullable
|
||||
@Nullable
|
||||
var avatar: Avatar? = null,
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
|
||||
@@ -7,6 +7,14 @@ server:
|
||||
session:
|
||||
tracking-modes: cookie
|
||||
|
||||
management:
|
||||
endpoints.web.exposure.include: '*'
|
||||
endpoint:
|
||||
pause:
|
||||
enabled: false
|
||||
restart:
|
||||
enabled: true
|
||||
|
||||
spring:
|
||||
# Workaround for https://github.com/vaadin/hilla/issues/842
|
||||
devtools.restart.additional-exclude: dev/hilla/openapi.json
|
||||
|
||||
Reference in New Issue
Block a user