mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-15 08:15:37 +00:00
Implement frontend structure
This commit is contained in:
@@ -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