mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Implement frontend structure
This commit is contained in:
+2
-1
@@ -44,4 +44,5 @@ out/
|
||||
/data/
|
||||
/backend/src/main/resources/static/
|
||||
/docker/docker-compose.yml
|
||||
/.gameyfin/
|
||||
/.gameyfin/
|
||||
/frontend/
|
||||
|
||||
+45
-34
@@ -1,63 +1,74 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("org.springframework.boot") version "3.2.2"
|
||||
id("io.spring.dependency-management") version "1.1.4"
|
||||
id("com.vaadin") version "24.3.3"
|
||||
kotlin("jvm") version "1.9.22"
|
||||
kotlin("plugin.spring") version "1.9.22"
|
||||
kotlin("plugin.jpa") version "1.9.22"
|
||||
id("org.springframework.boot") version "3.2.2"
|
||||
id("io.spring.dependency-management") version "1.1.4"
|
||||
id("com.vaadin") version "24.3.3"
|
||||
kotlin("jvm") version "1.9.22"
|
||||
kotlin("plugin.spring") version "1.9.22"
|
||||
kotlin("plugin.jpa") version "1.9.22"
|
||||
}
|
||||
|
||||
group = "de.grimsi"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
version = "2.0.0-SNAPSHOT"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom(configurations.annotationProcessor.get())
|
||||
}
|
||||
compileOnly {
|
||||
extendsFrom(configurations.annotationProcessor.get())
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "Vaadin Addons"
|
||||
url = uri("https://maven.vaadin.com/vaadin-addons")
|
||||
}
|
||||
}
|
||||
|
||||
extra["vaadinVersion"] = "24.3.3"
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("com.vaadin:vaadin-spring-boot-starter")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
runtimeOnly("com.h2database:h2")
|
||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.springframework.security:spring-security-test")
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
|
||||
|
||||
// Frontend
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("com.vaadin:vaadin-spring-boot-starter")
|
||||
implementation("com.github.mvysny.karibudsl:karibu-dsl-v23:2.1.0")
|
||||
implementation("com.github.mvysny.karibu-tools:karibu-tools-23:0.19")
|
||||
implementation("com.flowingcode.addons:font-awesome-iron-iconset:5.2.2")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
|
||||
// Development
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
runtimeOnly("com.h2database:h2")
|
||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.springframework.security:spring-security-test")
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom("com.vaadin:vaadin-bom:${property("vaadinVersion")}")
|
||||
}
|
||||
imports {
|
||||
mavenBom("com.vaadin:vaadin-bom:${property("vaadinVersion")}")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += "-Xjsr305=strict"
|
||||
jvmTarget = "21"
|
||||
}
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += "-Xjsr305=strict"
|
||||
jvmTarget = "21"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#-------------------------------------------------------------------------------#
|
||||
# Qodana analysis is configured by qodana.yaml file #
|
||||
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
|
||||
#-------------------------------------------------------------------------------#
|
||||
version: "1.0"
|
||||
|
||||
#Specify inspection profile for code analysis
|
||||
profile:
|
||||
name: qodana.starter
|
||||
|
||||
#Enable inspections
|
||||
#include:
|
||||
# - name: <SomeEnabledInspectionId>
|
||||
|
||||
#Disable inspections
|
||||
#exclude:
|
||||
# - name: <SomeDisabledInspectionId>
|
||||
# paths:
|
||||
# - <path/where/not/run/inspection>
|
||||
|
||||
projectJDK: "21" #(Applied in CI/CD pipeline)
|
||||
|
||||
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
|
||||
#bootstrap: sh ./prepare-qodana.sh
|
||||
|
||||
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
|
||||
#plugins:
|
||||
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
|
||||
|
||||
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
|
||||
linter: jetbrains/qodana-jvm:latest
|
||||
@@ -0,0 +1,32 @@
|
||||
This directory is automatically generated by Vaadin and contains the pre-compiled
|
||||
frontend files/resources for your project (frontend development bundle).
|
||||
|
||||
It should be added to Version Control System and committed, so that other developers
|
||||
do not have to compile it again.
|
||||
|
||||
Frontend development bundle is automatically updated when needed:
|
||||
- an npm/pnpm package is added with @NpmPackage or directly into package.json
|
||||
- CSS, JavaScript or TypeScript files are added with @CssImport, @JsModule or @JavaScript
|
||||
- Vaadin add-on with front-end customizations is added
|
||||
- Custom theme imports/assets added into 'theme.json' file
|
||||
- Exported web component is added.
|
||||
|
||||
If your project development needs a hot deployment of the frontend changes,
|
||||
you can switch Flow to use Vite development server (default in Vaadin 23.3 and earlier versions):
|
||||
- set `vaadin.frontend.hotdeploy=true` in `application.properties`
|
||||
- configure `vaadin-maven-plugin`:
|
||||
```
|
||||
<configuration>
|
||||
<frontendHotdeploy>true</frontendHotdeploy>
|
||||
</configuration>
|
||||
```
|
||||
- configure `jetty-maven-plugin`:
|
||||
```
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<vaadin.frontend.hotdeploy>true</vaadin.frontend.hotdeploy>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
Read more [about Vaadin development mode](https://vaadin.com/docs/next/configuration/development-mode/#pre-compiled-front-end-bundle-for-faster-start-up).
|
||||
Binary file not shown.
@@ -3,9 +3,10 @@ package de.grimsi.gameyfin
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
class GameyfinApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<GameyfinApplication>(*args)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package de.grimsi.gameyfin.security
|
||||
|
||||
import com.vaadin.flow.spring.security.VaadinWebSecurity
|
||||
import de.grimsi.gameyfin.ui.views.LoginView
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
import org.springframework.security.provisioning.UserDetailsManager
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
class SecurityConfiguration : VaadinWebSecurity() {
|
||||
@Throws(Exception::class)
|
||||
override fun configure(http: HttpSecurity) {
|
||||
// Configure your static resources with public access before calling super.configure(HttpSecurity) as it adds final anyRequest matcher
|
||||
http.authorizeHttpRequests { auth: AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry ->
|
||||
auth.requestMatchers(AntPathRequestMatcher("/public/**")).permitAll()
|
||||
}
|
||||
|
||||
// Configure your static resources with public access before calling
|
||||
// super.configure(HttpSecurity) as it adds final anyRequest matcher
|
||||
|
||||
super.configure(http)
|
||||
|
||||
// This is important to register your login view to the navigation access control mechanism:
|
||||
setLoginView(http, LoginView::class.java)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
public override fun configure(web: WebSecurity) {
|
||||
super.configure(web)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Just for testing
|
||||
*/
|
||||
@Bean
|
||||
fun userDetailsService(): UserDetailsManager {
|
||||
val user: UserDetails =
|
||||
User.withUsername("user")
|
||||
.password("{noop}user")
|
||||
.roles("USER")
|
||||
.build()
|
||||
val admin: UserDetails =
|
||||
User.withUsername("admin")
|
||||
.password("{noop}admin")
|
||||
.roles("ADMIN")
|
||||
.build()
|
||||
return InMemoryUserDetailsManager(user, admin)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.grimsi.gameyfin.security
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
|
||||
fun UserDetails.hasRole(role: String): Boolean {
|
||||
return this.authorities.map { a -> a.authority }.contains("ROLE_".plus(role))
|
||||
}
|
||||
|
||||
fun UserDetails.isAdmin(): Boolean {
|
||||
return hasRole("ADMIN")
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package de.grimsi.gameyfin.ui.layouts
|
||||
|
||||
import com.flowingcode.vaadin.addons.fontawesome.FontAwesome
|
||||
import com.github.mvysny.karibudsl.v10.*
|
||||
import com.github.mvysny.kaributools.tooltip
|
||||
import com.vaadin.flow.component.ClickEvent
|
||||
import com.vaadin.flow.component.applayout.AppLayout
|
||||
import com.vaadin.flow.component.menubar.MenuBarVariant
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout
|
||||
import com.vaadin.flow.router.RouterLayout
|
||||
import com.vaadin.flow.spring.security.AuthenticationContext
|
||||
import de.grimsi.gameyfin.security.isAdmin
|
||||
import de.grimsi.gameyfin.ui.resources.PublicResources
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
|
||||
|
||||
class MainLayout(@field:Transient private val authContext: AuthenticationContext) : KComposite(), RouterLayout {
|
||||
|
||||
|
||||
private val appLayout: AppLayout
|
||||
|
||||
init {
|
||||
val user = authContext.getAuthenticatedUser(UserDetails::class.java).get()
|
||||
|
||||
appLayout = ui {
|
||||
|
||||
appLayout {
|
||||
navbar {
|
||||
flexLayout {
|
||||
setWidthFull()
|
||||
alignItems = FlexComponent.Alignment.CENTER
|
||||
|
||||
image(PublicResources.GAMEYFIN_LOGO_WHITE_BORDER.path) {
|
||||
setWidthFull()
|
||||
height = "40px"
|
||||
className = "header-logo"
|
||||
}
|
||||
|
||||
horizontalLayout {
|
||||
alignItems = FlexComponent.Alignment.CENTER
|
||||
|
||||
val a = avatar(user.username) {
|
||||
tooltip = user.username
|
||||
abbreviation = user.username.take(2).uppercase()
|
||||
colorIndex = user.username[0].code.toByte().mod(6)
|
||||
}
|
||||
|
||||
menuBar {
|
||||
addThemeVariants(MenuBarVariant.LUMO_ICON)
|
||||
item(a) {
|
||||
item(menuItem(FontAwesome.Solid.USER, "Profile"))
|
||||
if (user.isAdmin()) item(menuItem(FontAwesome.Solid.COG, "Administration"))
|
||||
item(menuItem(FontAwesome.Solid.QUESTION_CIRCLE, "Help"))
|
||||
item(menuItem(FontAwesome.Solid.SIGN_OUT, "Sign out") { _ -> authContext.logout() })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun menuItem(icon: FontAwesome.Solid, title: String): HorizontalLayout {
|
||||
return HorizontalLayout().apply {
|
||||
alignItems = FlexComponent.Alignment.CENTER
|
||||
justifyContentMode = FlexComponent.JustifyContentMode.START
|
||||
|
||||
val faIcon = icon.create()
|
||||
faIcon.setSize("var(--lumo-icon-size-s)")
|
||||
add(faIcon)
|
||||
|
||||
text(title)
|
||||
}
|
||||
}
|
||||
|
||||
private fun menuItem(
|
||||
icon: FontAwesome.Solid,
|
||||
title: String,
|
||||
action: (ClickEvent<HorizontalLayout>) -> Unit
|
||||
): HorizontalLayout {
|
||||
return menuItem(icon, title).apply {
|
||||
onLeftClick(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.grimsi.gameyfin.ui.resources
|
||||
|
||||
enum class PublicResources(val path: String) {
|
||||
GAMEYFIN_LOGO_WHITE("public/images/Gameyfin_Logo_White.svg"),
|
||||
GAMEYFIN_LOGO_WHITE_BORDER("public/images/Gameyfin_Logo_White_BORDER.svg")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package de.grimsi.gameyfin.ui.views
|
||||
|
||||
import com.github.mvysny.karibudsl.v10.image
|
||||
import com.github.mvysny.karibudsl.v10.loginForm
|
||||
import com.vaadin.flow.component.login.LoginForm
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout
|
||||
import com.vaadin.flow.router.BeforeEnterEvent
|
||||
import com.vaadin.flow.router.BeforeEnterObserver
|
||||
import com.vaadin.flow.router.PageTitle
|
||||
import com.vaadin.flow.router.Route
|
||||
import com.vaadin.flow.server.auth.AnonymousAllowed
|
||||
import de.grimsi.gameyfin.ui.resources.PublicResources
|
||||
|
||||
@Route("login")
|
||||
@PageTitle("Login")
|
||||
@AnonymousAllowed
|
||||
class LoginView : VerticalLayout(), BeforeEnterObserver {
|
||||
|
||||
private var login: LoginForm
|
||||
|
||||
init {
|
||||
setSizeFull()
|
||||
justifyContentMode = FlexComponent.JustifyContentMode.CENTER
|
||||
alignItems = FlexComponent.Alignment.CENTER
|
||||
|
||||
image {
|
||||
height = "100px"
|
||||
src = PublicResources.GAMEYFIN_LOGO_WHITE_BORDER.path
|
||||
setAlt("Gameyfin")
|
||||
}
|
||||
|
||||
login = loginForm {
|
||||
addClassName("login-view")
|
||||
action = "login"
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeEnter(event: BeforeEnterEvent?) {
|
||||
if (event != null) {
|
||||
if (event.location
|
||||
.queryParameters
|
||||
.parameters
|
||||
.containsKey("error")) {
|
||||
login.isError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.grimsi.gameyfin.ui.views
|
||||
|
||||
import com.github.mvysny.karibudsl.v10.h1
|
||||
import com.github.mvysny.karibudsl.v10.pre
|
||||
import com.github.mvysny.karibudsl.v10.verticalLayout
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout
|
||||
import com.vaadin.flow.router.Route
|
||||
import de.grimsi.gameyfin.ui.layouts.MainLayout
|
||||
import jakarta.annotation.security.PermitAll
|
||||
|
||||
|
||||
@Route("", layout = MainLayout::class)
|
||||
@PermitAll
|
||||
class MainView : VerticalLayout() {
|
||||
|
||||
init {
|
||||
verticalLayout {
|
||||
h1 { text = "Gameyfin main page" }
|
||||
pre { text = "Work in progress" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 450 414">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fff;}</style>
|
||||
</defs>
|
||||
<title>Element 11</title>
|
||||
<g>
|
||||
<g>
|
||||
<polygon class="cls-1" points="234 60.48 234 85.23 256 54.32 234 60.48"/>
|
||||
<polygon class="cls-1" points="450 0 324 35.28 253.125 118.125 450 63 450 0"/>
|
||||
<polygon class="cls-1" points="234 348.48 306 328.32 306 184.32 450 144 450 90 234 150.48 234 348.48"/>
|
||||
<polygon class="cls-1" points="72 177.84 192 144.24 216 110.52 216 65.52 0 126 0 414 72 312.84 72 177.84"/>
|
||||
<polygon class="cls-1"
|
||||
points="144 245.68 144 301.68 81 319.32 0 414 216 353.52 216 209.52 162 224.64 144 245.68"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 768 B |
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 451.94942 416.224">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fff;stroke:#000;stroke-miterlimit:10;}</style>
|
||||
</defs>
|
||||
<title>Element 10</title>
|
||||
<g>
|
||||
<g>
|
||||
<polygon class="cls-1" points="235.449 61.139 235.449 85.889 257.449 54.979 235.449 61.139"/>
|
||||
<polygon class="cls-1" points="451.449 0.659 325.449 35.939 254.574 118.784 451.449 63.659 451.449 0.659"/>
|
||||
<polygon class="cls-1"
|
||||
points="235.449 349.139 307.449 328.979 307.449 184.979 451.449 144.659 451.449 90.659 235.449 151.139 235.449 349.139"/>
|
||||
<polygon class="cls-1"
|
||||
points="73.449 178.499 193.449 144.899 217.449 111.179 217.449 66.179 1.449 126.659 1.449 414.659 73.449 313.499 73.449 178.499"/>
|
||||
<polygon class="cls-1"
|
||||
points="145.449 246.339 145.449 302.339 82.449 319.979 1.449 414.659 217.449 354.179 217.449 210.179 163.449 225.299 145.449 246.339"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1 +0,0 @@
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
vaadin.whitelisted-packages:
|
||||
- com.vaadin
|
||||
- org.vaadin
|
||||
- dev.hilla
|
||||
- com.flowingcode
|
||||
Reference in New Issue
Block a user