* Improves document display

* Adds Roles
* Adds Role based auth
This commit is contained in:
Stefan Schallerl 2025-02-03 11:47:55 +01:00
parent aa940cc42a
commit 68cbb94977
9 changed files with 114 additions and 24 deletions

View file

@ -1,8 +1,11 @@
package net.h34t.filemure
import io.javalin.Javalin
import io.javalin.http.UnauthorizedResponse
import net.h34t.filemure.controller.*
import net.h34t.filemure.repository.SqliteRepository
import net.h34t.filemure.tpl.Frame
import net.h34t.filemure.tpl.Unauthorized
class FilemureApp(repository: SqliteRepository) {
@ -16,20 +19,36 @@ class FilemureApp(repository: SqliteRepository) {
private val documentController = DocumentController(modifiers, repository)
fun register(server: Javalin) {
server.get("/") { ctx ->
if (ctx.getSession() != null) {
overviewController.overview(ctx)
} else {
server.beforeMatched { ctx ->
val userRole = getUserRole(ctx)
if (!ctx.routeRoles().contains(userRole)) {
ctx.redirectPRG("/login")
throw UnauthorizedResponse()
}
}
server.get("/login", loginPageController::formLogin)
server.post("/login", loginPageController::doLogin)
server.post("/logout", loginPageController::doLogout)
server.post("/upload", uploadController::upload)
server.get("/limbo", limboController::formLimbo)
server.get("/document/new", documentController::createDocumentForm)
server.post("/document/new", documentController::createDocument)
server.get("/", overviewController::overview, Role.USER)
server.get("/login", loginPageController::formLogin, Role.ANON, Role.USER)
server.post("/login", loginPageController::doLogin, Role.ANON, Role.USER)
server.post("/logout", loginPageController::doLogout, Role.USER)
server.post("/upload", uploadController::upload, Role.USER)
server.get("/limbo", limboController::formLimbo, Role.USER)
server.get("/document/new", documentController::createDocumentForm, Role.USER)
server.post("/document/new", documentController::createDocument, Role.USER)
server.get("/document/{extId}", documentController::documentDetail, Role.USER)
server.exception(UnauthorizedResponse::class.java) { e, ctx ->
ctx.tempolin(
Frame(
modifiers = modifiers,
title = "unauthorized",
isTarget = false,
content = Unauthorized()
)
)
}
}
}

View file

@ -0,0 +1,18 @@
package net.h34t.filemure
import io.javalin.http.Context
import io.javalin.security.RouteRole
enum class Role : RouteRole {
ANON, USER, ADMIN
}
fun getUserRole(ctx: Context): Role {
val session = ctx.getSession()
return if (session == null) {
Role.ANON
} else {
Role.USER
}
}

View file

@ -1,12 +1,11 @@
package net.h34t.filemure
import net.h34t.filemure.tpl.Frame
import net.h34t.filemure.tpl.Limbo
import net.h34t.filemure.tpl.NewDocumentForm
import net.h34t.filemure.tpl.*
import org.apache.commons.text.StringEscapeUtils
import java.net.URLEncoder
class TemplateModifiers : Frame.Modifiers, Limbo.Modifiers, NewDocumentForm.Modifiers {
class TemplateModifiers : Frame.Modifiers, Limbo.Modifiers, NewDocumentForm.Modifiers, Overview.Modifiers,
Document.Modifiers {
fun hashPrefix(arg: String): String {
return URLEncoder.encode(arg, Charsets.UTF_8)

View file

@ -5,6 +5,7 @@ import io.javalin.http.Context
import io.javalin.http.ForbiddenResponse
import net.h34t.filemure.*
import net.h34t.filemure.repository.SqliteRepository
import net.h34t.filemure.tpl.Document
import net.h34t.filemure.tpl.Frame
import net.h34t.filemure.tpl.NewDocumentForm
import java.io.File
@ -21,6 +22,31 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
val session = ctx.requireSession()
val extId = ctx.pathParam("extId")
val document = repository.getDocumentByExtId(accountId = session.id, extId = extId)
ctx.tempolin(
Frame(
modifiers = modifiers,
title = document.title,
isTarget = true,
content = Document(
modifiers = modifiers,
title = document.title,
referenceDate = dtf.format(document.referenceDate),
tags = { document.tags.map { TagsBlock(tag = it.value) }.asSequence() },
description = document.description,
files = {
document.files.map { file ->
FilesBlock(
extId = file.extId,
filename = file.filename,
contentType = file.contentType ?: "?"
)
}.asSequence()
},
)
)
)
}

View file

@ -7,21 +7,37 @@ import net.h34t.filemure.requireSession
import net.h34t.filemure.tempolin
import net.h34t.filemure.tpl.Frame
import net.h34t.filemure.tpl.Overview
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
class OverviewController(val modifiers: TemplateModifiers, val repository: SqliteRepository) {
val dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
fun overview(ctx: Context) {
val session = ctx.requireSession()
val limboFileCount = repository.getLimboFileCount(accountId = session.id)
val documents = repository.getDocuments(accountId = session.id)
ctx.tempolin(
Frame(
modifiers = modifiers,
title = "Filemure Overview",
isTarget = true,
content = Overview(
limboFileCount = limboFileCount.toString()
modifiers = modifiers,
limboFileCount = limboFileCount.toString(),
document = {
documents.map { document ->
DocumentBlock(
extId = document.extId,
referenceDate = dtf.format(document.referenceDate),
title = document.title.ifBlank { "untitled" },
)
}.asSequence()
}
)
)
)

View file

@ -1,6 +1,6 @@
<h1>{*$title}</h1>
<p>Date: {*$referenceDate}"></p>
<p>Date: {*$referenceDate}</p>
<p>Tags: <span id="tags">{for $tags}<span>{*$tag}</span>{/for}</span></p>

View file

@ -1,4 +1,13 @@
<h1>Hello to Filemure</h1>
Files in <a href="/limbo">limbo: {$limboFileCount}</a>.
<p>Files in <a href="/limbo">limbo: {$limboFileCount}</a>.</p>
<table>
{for $document}
<tr>
<td>{*$referenceDate}</td>
<td>{*$title}</td>
<td><a href="/document/{*$extId}">details</a></td>
</tr>
{/for}
</table>

View file

@ -0,0 +1,3 @@
<h1>Unauthorized</h1>
<p><a href="/login">Please log in</a>.</p>

View file

@ -1,7 +1,6 @@
package net.h34t.filemure.core.entity
import java.time.LocalDateTime
import java.util.*
class Document(
val id: Long,
@ -11,7 +10,7 @@ class Document(
val tags: List<Tag>,
val created: LocalDateTime,
val referenceDate: LocalDateTime,
val files: List<DocFile>,
val files: List<FileRef>,
)
@JvmInline
@ -25,10 +24,11 @@ value class MimeType(val value: String) {
}
}
data class DocFile(
val id: UUID,
data class FileRef(
val id: Long,
val extId: String,
val filename: String,
val mimeType: MimeType,
val contentType: String?,
val created: LocalDateTime,
)