aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin Borner2019-04-16 23:52:59 +0200
committerMarvin Borner2019-04-16 23:52:59 +0200
commitcdd6fa1981ebf8d96e4849167805c471451e5d6d (patch)
tree80d9ac1670d6f08a5ec0bd540fd9d96a8fa2be6f
parentf84c2ada032efee0b9d9eb03c63b10458f135dcd (diff)
Abstracted functions to classes
Co-authored-by: LarsVomMars <lars@kroenner.eu>
-rw-r--r--build.gradle14
-rw-r--r--src/main/kotlin/App.kt285
-rw-r--r--src/main/kotlin/FileController.kt165
-rw-r--r--src/main/kotlin/UserHandler.kt109
-rw-r--r--src/main/resources/css/layout.css2
-rw-r--r--src/main/resources/js/files.js4
6 files changed, 308 insertions, 271 deletions
diff --git a/build.gradle b/build.gradle
index 1eee8c8..1cab197 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,3 +1,12 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin"
+ }
+}
+
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.11'
id 'nu.studer.rocker' version '0.4'
@@ -7,6 +16,10 @@ plugins {
group "space.anity"
version "1.0-SNAPSHOT"
+apply plugin: 'kotlin'
+apply plugin: 'application'
+mainClassName = 'space.anity.AppKt'
+
repositories {
jcenter()
}
@@ -35,6 +48,7 @@ dependencies {
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
+
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
diff --git a/src/main/kotlin/App.kt b/src/main/kotlin/App.kt
index 2789530..ac9127b 100644
--- a/src/main/kotlin/App.kt
+++ b/src/main/kotlin/App.kt
@@ -5,24 +5,17 @@ import com.fizzed.rocker.runtime.*
import io.javalin.*
import io.javalin.Handler
import io.javalin.apibuilder.ApiBuilder.*
-import io.javalin.core.util.*
import io.javalin.rendering.*
import io.javalin.rendering.template.TemplateUtil.model
import io.javalin.security.*
import io.javalin.security.SecurityUtil.roles
-import org.joda.time.*
-import java.io.*
-import java.nio.charset.*
-import java.nio.file.*
-import java.text.*
-import java.util.*
import java.util.logging.*
-import kotlin.math.*
-
const val fileHome = "files"
val databaseController = DatabaseController()
-private val log = Logger.getLogger("App.kt")
+val userHandler = UserHandler()
+val fileController = FileController()
+val log = Logger.getLogger("App.kt")
fun main() {
val app = Javalin.create()
@@ -48,7 +41,7 @@ fun main() {
{ ctx ->
ctx.render(
"index.rocker.html",
- model("username", databaseController.getUsername(getVerifiedUserId(ctx)))
+ model("username", databaseController.getUsername(userHandler.getVerifiedUserId(ctx)))
)
},
roles(Roles.GUEST)
@@ -58,7 +51,7 @@ fun main() {
* Renders the login page
*/
get("/login", { ctx ->
- if (getVerifiedUserId(ctx) > 0) ctx.redirect("/")
+ if (userHandler.getVerifiedUserId(ctx) > 0) ctx.redirect("/")
else ctx.render(
"login.rocker.html",
model("message", "", "counter", 0)
@@ -68,12 +61,12 @@ fun main() {
/**
* Endpoint for user authentication
*/
- post("/login", ::login, roles(Roles.GUEST))
+ post("/login", userHandler::login, roles(Roles.GUEST))
/**
* Logs the user out
*/
- get("/logout", ::logout, roles(Roles.USER))
+ get("/logout", userHandler::logout, roles(Roles.USER))
/**
* Renders the setup page (only on initial use)
@@ -89,34 +82,34 @@ fun main() {
/**
* Endpoint for setup (only on initial use)
*/
- post("/setup", ::setup, roles(Roles.GUEST))
+ post("/setup", userHandler::setup, roles(Roles.GUEST))
/**
* Renders the file list view
* TODO: Fix possible security issue with "../"
*/
- get("/files/*", ::crawlFiles, roles(Roles.USER))
+ get("/files/*", fileController::crawl, roles(Roles.USER))
/**
* Receives and saves multipart media data
* TODO: Fix possible security issue with "../"
*/
- post("/upload/*", ::upload, roles(Roles.USER))
+ post("/upload/*", fileController::upload, roles(Roles.USER))
/**
* Deletes file
*/
- post("/delete/*", ::delete, roles(Roles.USER))
+ post("/delete/*", fileController::delete, roles(Roles.USER))
/**
* Shares file
*/
- post("/share/*", ::shareFile, roles(Roles.USER))
+ post("/share/*", fileController::share, roles(Roles.USER))
/**
* Shows the shared file
*/
- get("/shared", ::renderSharedFile, roles(Roles.GUEST))
+ get("/shared", fileController::renderShared, roles(Roles.GUEST))
}
}
@@ -125,99 +118,15 @@ fun main() {
*/
fun roleManager(handler: Handler, ctx: Context, permittedRoles: Set<Role>) {
when {
- getVerifiedUserId(ctx) == ctx.cookieStore("userId") ?: "userId" -> handler.handle(ctx)
- databaseController.getRoles(getVerifiedUserId(ctx)).any { it in permittedRoles } -> handler.handle(ctx)
+ userHandler.getVerifiedUserId(ctx) == ctx.cookieStore("userId") ?: "userId" -> handler.handle(ctx)
+ databaseController.getRoles(userHandler.getVerifiedUserId(ctx)).any { it in permittedRoles } -> handler.handle(
+ ctx
+ )
//ctx.host()!!.contains("localhost") -> handler.handle(ctx) // DEBUG
else -> ctx.status(401).redirect("/login")
}
}
-/**
- * Gets the username and verifies its identity
- */
-fun getVerifiedUserId(ctx: Context): Int {
- return if (databaseController.getUserIdByVerificationId(ctx.cookieStore("verification") ?: "verification")
- == ctx.cookieStore("userId") ?: "userId"
- ) ctx.cookieStore("userId")
- else -1
-}
-
-/**
- * Crawls the requested file and either renders the directory view or the file view
- */
-fun crawlFiles(ctx: Context) {
- try {
- val usersFileHome = "$fileHome/${getVerifiedUserId(ctx)}"
- File(usersFileHome).mkdirs()
- when {
- File("$usersFileHome/${ctx.splats()[0]}").isDirectory -> {
- val files = ArrayList<Array<String>>()
- Files.list(Paths.get("$usersFileHome/${ctx.splats()[0]}/")).forEach {
- val fileName = it.toString()
- .drop(usersFileHome.length + (if (ctx.splats()[0].isNotEmpty()) ctx.splats()[0].length + 2 else 1))
- val filePath = "$usersFileHome${it.toString().drop(usersFileHome.length)}"
- val file = File(filePath)
- val fileSize = if (file.isDirectory) getDirectorySize(file) else file.length()
- files.add(
- // TODO: Clean up file array responses
- arrayOf(
- if (file.isDirectory) "$fileName/" else fileName,
- humanReadableBytes(fileSize),
- SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(file.lastModified()).toString(),
- if (file.isDirectory) "true" else isHumanReadable(file).toString(),
- fileSize.toString(), // unformatted file size
- file.lastModified().toString() // unformatted last modified date
- )
- )
- }
- //files.sortWith(String.CASE_INSENSITIVE_ORDER) // TODO: Reimplement file array sorting in backend
- ctx.render(
- "files.rocker.html", model(
- "files", files,
- "path", ctx.splats()[0]
- )
- )
- }
- isHumanReadable(File("$usersFileHome/${ctx.splats()[0]}")) ->
- ctx.render(
- "fileview.rocker.html", model(
- "content", Files.readAllLines(
- Paths.get("$usersFileHome/${ctx.splats()[0]}"),
- Charsets.UTF_8
- ).joinToString(separator = "\n"),
- "filename", File("$usersFileHome/${ctx.splats()[0]}").name,
- "extension", File("$usersFileHome/${ctx.splats()[0]}").extension
- )
- )
- else -> ctx.result(FileInputStream(File("$usersFileHome/${ctx.splats()[0]}")))
- }
- } catch (_: Exception) {
- throw NotFoundResponse("Error: File or directory does not exist.")
- }
-}
-
-/**
- * Gets directory size recursively
- */
-fun getDirectorySize(directory: File): Long {
- var length: Long = 0
- for (file in directory.listFiles()!!) {
- length += if (file.isFile) file.length()
- else getDirectorySize(file)
- }
- return length
-}
-
-/**
- * Saves multipart media data into requested directory
- */
-fun upload(ctx: Context) {
- ctx.uploadedFiles("file").forEach { (_, content, name, _) ->
- val path = "${ctx.splats()[0]}/$name"
- FileUtil.streamToFile(content, "$fileHome/${getVerifiedUserId(ctx)}/$path")
- databaseController.addFile(path, getVerifiedUserId(ctx))
- }
-}
/*
fun indexAllFiles(ctx: Context) {
Files.list(Paths.get("$fileHome/${getVerifiedUserId(ctx)}").forEach {
@@ -227,166 +136,6 @@ fun indexAllFiles(ctx: Context) {
*/
/**
- * Checks whether the file is binary or human-readable (text)
- */
-private fun isHumanReadable(file: File): Boolean {
- val input = FileInputStream(file)
- var size = input.available()
- if (size > 1000) size = 1000
- val data = ByteArray(size)
- input.read(data)
- input.close()
- val text = String(data, Charset.forName("ISO-8859-1"))
- val replacedText = text.replace(
- ("[a-zA-Z0-9ßöäü\\.\\*!\"§\\$\\%&/()=\\?@~'#:,;\\+><\\|\\[\\]\\{\\}\\^°²³\\\\ \\n\\r\\t_\\-`´âêîôÂÊÔÎáéíóàèìòÁÉÍÓÀÈÌÒ©‰¢£¥€±¿»«¼½¾™ª]").toRegex(),
- ""
- )
- val d = (text.length - replacedText.length).toDouble() / text.length.toDouble()
- return d > 0.95
-}
-
-fun humanReadableBytes(bytes: Long): String {
- val unit = 1024
- if (bytes < unit) return "$bytes B"
- val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
- val pre = "KMGTPE"[exp - 1] + "i"
- return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre)
-}
-
-/**
- * Checks and verifies users credentials and logs the user in
- */
-fun login(ctx: Context) {
- if (getVerifiedUserId(ctx) > 0) ctx.redirect("/")
-
- val username = ctx.formParam("username").toString()
- val password = ctx.formParam("password").toString()
- val requestIp = ctx.ip()
-
- val loginAttempts = databaseController.getLoginAttempts(requestIp)
- val lastAttemptDifference =
- if (loginAttempts.isEmpty())
- -1
- else Interval(loginAttempts[loginAttempts.indexOfLast { true }].first.toInstant(), Instant()).toDuration()
- .standardSeconds.toInt()
-
- var lastHourAttempts = 0
- loginAttempts.forEach {
- val difference = Interval(it.first.toInstant(), Instant()).toDuration().standardMinutes.toInt()
- if (difference < 60) lastHourAttempts += 1
- }
- val nextThreshold = 4f.pow(lastHourAttempts + 1)
-
- if (lastAttemptDifference > 4f.pow(lastHourAttempts) || lastHourAttempts == 0) {
- if (databaseController.checkUser(username, password)) {
- ctx.cookieStore("verification", databaseController.getVerificationId(username))
- ctx.cookieStore("userId", databaseController.getUserId(username))
- ctx.redirect("/")
- } else {
- databaseController.loginAttempt(DateTime(), requestIp)
- ctx.render(
- "login.rocker.html",
- model(
- "message",
- "Login failed!",
- "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt()
- )
- )
- }
- } else {
- databaseController.loginAttempt(DateTime(), requestIp)
- ctx.render(
- "login.rocker.html",
- model(
- "message",
- "Too many request.",
- "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt()
- )
- )
- }
-}
-
-/**
- * Logs the user out of the system
- */
-fun logout(ctx: Context) {
- ctx.clearCookieStore()
- ctx.redirect("/")
-}
-
-/**
- * Sets up the general settings and admin credentials
- */
-fun setup(ctx: Context) {
- if (databaseController.isSetup()) ctx.render(
- "setup.rocker.html",
- model("message", "Setup process already finished!")
- ) else {
- try {
- val username = ctx.formParam("username").toString()
- val password = ctx.formParam("password").toString()
- val verifyPassword = ctx.formParam("verifyPassword").toString()
- if (password == verifyPassword) {
- if (databaseController.createUser(username, password, "ADMIN")) {
- databaseController.toggleSetup()
- ctx.render("setup.rocker.html", model("message", "Setup succeeded!"))
- } else ctx.status(400).render("setup.rocker.html", model("message", "User already exists!"))
- } else ctx.status(400).render("setup.rocker.html", model("message", "Passwords do not match!"))
- } catch (_: Exception) {
- ctx.status(400).render("setup.rocker.html", model("message", "An error occurred!"))
- }
- }
-}
-
-/**
- * Deletes the requested file
- */
-fun delete(ctx: Context) {
- val userId = getVerifiedUserId(ctx)
- if (userId > 0) {
- val path = ctx.splats()[0]
- File("$fileHome/$userId/$path").delete()
- databaseController.deleteFile(path, userId)
- }
-}
-
-/**
- * Shares the requested file via the accessId
- */
-fun shareFile(ctx: Context) {
- val userId = getVerifiedUserId(ctx)
- if (userId > 0) {
- val accessId = databaseController.getAccessId(ctx.splats()[0], userId)
- ctx.result("${ctx.host()}/shared?id=$accessId")
- }
-}
-
-/**
- * Renders the shared file
- */
-fun renderSharedFile(ctx: Context) {
- val accessId = ctx.queryParam("id").toString()
- val sharedFileData = databaseController.getSharedFile(accessId)
- if (sharedFileData.first > 0 && sharedFileData.second.isNotEmpty()) {
- val sharedFileLocation = "$fileHome/${sharedFileData.first}/${sharedFileData.second}"
- if (isHumanReadable(File(sharedFileLocation))) {
- ctx.render(
- "fileview.rocker.html", model(
- "content", Files.readAllLines(
- Paths.get(sharedFileLocation),
- Charsets.UTF_8
- ).joinToString(separator = "\n"),
- "filename", File(sharedFileLocation).name,
- "extension", File(sharedFileLocation).extension
- )
- )
- } else ctx.result(FileInputStream(File(sharedFileLocation)))
- } else {
- log.info("Unknown file!")
- }
-}
-
-/**
* Declares the roles in which a user can be in
*/
enum class Roles : Role {
diff --git a/src/main/kotlin/FileController.kt b/src/main/kotlin/FileController.kt
new file mode 100644
index 0000000..08f0763
--- /dev/null
+++ b/src/main/kotlin/FileController.kt
@@ -0,0 +1,165 @@
+package space.anity
+
+import io.javalin.*
+import io.javalin.core.util.*
+import io.javalin.rendering.template.*
+import java.io.*
+import java.nio.charset.*
+import java.nio.file.*
+import java.text.*
+import java.util.*
+
+class FileController {
+ /**
+ * Crawls the requested file and either renders the directory view or the file view
+ */
+ fun crawl(ctx: Context) {
+ try {
+ val usersFileHome = "$fileHome/${userHandler.getVerifiedUserId(ctx)}"
+ File(usersFileHome).mkdirs()
+ when {
+ File("$usersFileHome/${ctx.splats()[0]}").isDirectory -> {
+ val files = ArrayList<Array<String>>()
+ Files.list(Paths.get("$usersFileHome/${ctx.splats()[0]}/")).forEach {
+ val fileName = it.toString()
+ .drop(usersFileHome.length + (if (ctx.splats()[0].isNotEmpty()) ctx.splats()[0].length + 2 else 1))
+ val filePath = "$usersFileHome${it.toString().drop(usersFileHome.length)}"
+ val file = File(filePath)
+ val fileSize = if (file.isDirectory) getDirectorySize(file) else file.length()
+ files.add(
+ // TODO: Clean up file array responses
+ arrayOf(
+ if (file.isDirectory) "$fileName/" else fileName,
+ humanReadableBytes(fileSize),
+ SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(file.lastModified()).toString(),
+ if (file.isDirectory) "true" else isHumanReadable(file).toString(),
+ fileSize.toString(), // unformatted file size
+ file.lastModified().toString() // unformatted last modified date
+ )
+ )
+ }
+ //files.sortWith(String.CASE_INSENSITIVE_ORDER) // TODO: Reimplement file array sorting in backend
+ ctx.render(
+ "files.rocker.html", TemplateUtil.model(
+ "files", files,
+ "path", ctx.splats()[0]
+ )
+ )
+ }
+ isHumanReadable(File("$usersFileHome/${ctx.splats()[0]}")) ->
+ ctx.render(
+ "fileview.rocker.html", TemplateUtil.model(
+ "content", Files.readAllLines(
+ Paths.get("$usersFileHome/${ctx.splats()[0]}"),
+ Charsets.UTF_8
+ ).joinToString(separator = "\n"),
+ "filename", File("$usersFileHome/${ctx.splats()[0]}").name,
+ "extension", File("$usersFileHome/${ctx.splats()[0]}").extension
+ )
+ )
+ else -> ctx.result(FileInputStream(File("$usersFileHome/${ctx.splats()[0]}")))
+ }
+ } catch (_: Exception) {
+ throw NotFoundResponse("Error: File or directory does not exist.")
+ }
+ }
+
+ /**
+ * Gets directory size recursively
+ */
+ private fun getDirectorySize(directory: File): Long {
+ var length: Long = 0
+ for (file in directory.listFiles()!!) {
+ length += if (file.isFile) file.length()
+ else getDirectorySize(file)
+ }
+ return length
+ }
+
+ /**
+ * Checks whether the file is binary or human-readable (text)
+ */
+ private fun isHumanReadable(file: File): Boolean {
+ val input = FileInputStream(file)
+ var size = input.available()
+ if (size > 1000) size = 1000
+ val data = ByteArray(size)
+ input.read(data)
+ input.close()
+ val text = String(data, Charset.forName("ISO-8859-1"))
+ val replacedText = text.replace(
+ ("[a-zA-Z0-9ßöäü\\.\\*!\"§\\$\\%&/()=\\?@~'#:,;\\+><\\|\\[\\]\\{\\}\\^°²³\\\\ \\n\\r\\t_\\-`´âêîôÂÊÔÎáéíóàèìòÁÉÍÓÀÈÌÒ©‰¢£¥€±¿»«¼½¾™ª]").toRegex(),
+ ""
+ )
+ val d = (text.length - replacedText.length).toDouble() / text.length.toDouble()
+ return d > 0.95
+ }
+
+ private fun humanReadableBytes(bytes: Long): String {
+ val unit = 1024
+ if (bytes < unit) return "$bytes B"
+ val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
+ val pre = "KMGTPE"[exp - 1] + "i"
+ return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre)
+ }
+
+ /**
+ * Saves multipart media data into requested directory
+ */
+ fun upload(ctx: Context) {
+ ctx.uploadedFiles("file").forEach { (_, content, name, _) ->
+ val path = "${ctx.splats()[0]}/$name"
+ FileUtil.streamToFile(content, "$fileHome/${userHandler.getVerifiedUserId(ctx)}/$path")
+ databaseController.addFile(path, userHandler.getVerifiedUserId(ctx))
+ }
+ }
+
+ /**
+ * Deletes the requested file
+ */
+ fun delete(ctx: Context) {
+ val userId = userHandler.getVerifiedUserId(ctx)
+ if (userId > 0) {
+ val path = ctx.splats()[0]
+ File("$fileHome/$userId/$path").delete()
+ databaseController.deleteFile(path, userId)
+ }
+ }
+
+ /**
+ * Shares the requested file via the accessId
+ */
+ fun share(ctx: Context) {
+ val userId = userHandler.getVerifiedUserId(ctx)
+ if (userId > 0) {
+ val accessId = databaseController.getAccessId(ctx.splats()[0], userId)
+ ctx.result("${ctx.host()}/shared?id=$accessId")
+ }
+ }
+
+ /**
+ * Renders the shared file
+ */
+ fun renderShared(ctx: Context) {
+ val accessId = ctx.queryParam("id").toString()
+ val sharedFileData = databaseController.getSharedFile(accessId)
+ if (sharedFileData.first > 0 && sharedFileData.second.isNotEmpty()) {
+ val sharedFileLocation = "$fileHome/${sharedFileData.first}/${sharedFileData.second}"
+ if (isHumanReadable(File(sharedFileLocation))) {
+ ctx.render(
+ "fileview.rocker.html", TemplateUtil.model(
+ "content", Files.readAllLines(
+ Paths.get(sharedFileLocation),
+ Charsets.UTF_8
+ ).joinToString(separator = "\n"),
+ "filename", File(sharedFileLocation).name,
+ "extension", File(sharedFileLocation).extension
+ )
+ )
+ } else ctx.result(FileInputStream(File(sharedFileLocation))) // TODO: other file types
+ } else {
+ log.info("Unknown file!")
+ }
+ }
+
+}
diff --git a/src/main/kotlin/UserHandler.kt b/src/main/kotlin/UserHandler.kt
new file mode 100644
index 0000000..8197427
--- /dev/null
+++ b/src/main/kotlin/UserHandler.kt
@@ -0,0 +1,109 @@
+package space.anity
+
+import io.javalin.*
+import io.javalin.rendering.template.*
+import org.joda.time.*
+import kotlin.math.*
+
+class UserHandler {
+ /**
+ * Checks and verifies users credentials and logs the user in
+ */
+ fun login(ctx: Context) {
+ if (getVerifiedUserId(ctx) > 0) ctx.redirect("/")
+
+ val username = ctx.formParam("username").toString()
+ val password = ctx.formParam("password").toString()
+ val requestIp = ctx.ip()
+
+ val loginAttempts = databaseController.getLoginAttempts(requestIp)
+ val lastAttemptDifference =
+ if (loginAttempts.isEmpty())
+ -1
+ else Interval(loginAttempts[loginAttempts.indexOfLast { true }].first.toInstant(), Instant()).toDuration()
+ .standardSeconds.toInt()
+
+ var lastHourAttempts = 0
+ loginAttempts.forEach {
+ val difference = Interval(it.first.toInstant(), Instant()).toDuration().standardMinutes.toInt()
+ if (difference < 60) lastHourAttempts += 1
+ }
+ val nextThreshold = 4f.pow(lastHourAttempts + 1)
+
+ if (lastAttemptDifference > 4f.pow(lastHourAttempts) || lastHourAttempts == 0) {
+ if (databaseController.checkUser(username, password)) {
+ ctx.cookieStore("verification", databaseController.getVerificationId(username))
+ ctx.cookieStore("userId", databaseController.getUserId(username))
+ ctx.redirect("/")
+ } else {
+ databaseController.loginAttempt(DateTime(), requestIp)
+ ctx.render(
+ "login.rocker.html",
+ TemplateUtil.model(
+ "message",
+ "Login failed!",
+ "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt()
+ )
+ )
+ }
+ } else {
+ databaseController.loginAttempt(DateTime(), requestIp)
+ ctx.render(
+ "login.rocker.html",
+ TemplateUtil.model(
+ "message",
+ "Too many request.",
+ "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt()
+ )
+ )
+ }
+ }
+
+ /**
+ * Logs the user out of the system
+ */
+ fun logout(ctx: Context) {
+ ctx.clearCookieStore()
+ ctx.redirect("/")
+ }
+
+ /**
+ * Sets up the general settings and admin credentials
+ */
+ fun setup(ctx: Context) {
+ if (databaseController.isSetup()) ctx.render(
+ "setup.rocker.html",
+ TemplateUtil.model("message", "Setup process already finished!")
+ ) else {
+ try {
+ val username = ctx.formParam("username").toString()
+ val password = ctx.formParam("password").toString()
+ val verifyPassword = ctx.formParam("verifyPassword").toString()
+ if (password == verifyPassword) {
+ if (databaseController.createUser(username, password, "ADMIN")) {
+ databaseController.toggleSetup()
+ ctx.render("setup.rocker.html", TemplateUtil.model("message", "Setup succeeded!"))
+ } else ctx.status(400).render(
+ "setup.rocker.html",
+ TemplateUtil.model("message", "User already exists!")
+ )
+ } else ctx.status(400).render(
+ "setup.rocker.html",
+ TemplateUtil.model("message", "Passwords do not match!")
+ )
+ } catch (_: Exception) {
+ ctx.status(400).render("setup.rocker.html", TemplateUtil.model("message", "An error occurred!"))
+ }
+ }
+ }
+
+ /**
+ * Gets the username and verifies its identity
+ */
+ fun getVerifiedUserId(ctx: Context): Int {
+ return if (databaseController.getUserIdByVerificationId(ctx.cookieStore("verification") ?: "verification")
+ == ctx.cookieStore("userId") ?: "userId"
+ ) ctx.cookieStore("userId")
+ else -1
+ }
+}
diff --git a/src/main/resources/css/layout.css b/src/main/resources/css/layout.css
index 2640419..5db8bff 100644
--- a/src/main/resources/css/layout.css
+++ b/src/main/resources/css/layout.css
@@ -4,7 +4,7 @@ html, body {
padding: 0;
margin: 0;
min-width: 100vmin;
- min-height: 100vmin;
+ min-height: 100%;
}
button {
diff --git a/src/main/resources/js/files.js b/src/main/resources/js/files.js
index f776165..28d091b 100644
--- a/src/main/resources/js/files.js
+++ b/src/main/resources/js/files.js
@@ -141,14 +141,14 @@ function setListeners() {
request.open("POST", `/share/${path}/${fileName}`);
request.onload = () => {
if (request.readyState === 4) {
- if (request.status === 200) {
+ if (request.status === 200) { // TODO: fix clipboard in Firefox
const input = document.createElement('input');
input.setAttribute('value', request.responseText);
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
- alert("Copied url to clipboard!")
+ alert(`Copied url to clipboard!\n${request.responseText}`);
} else {
alert("Something went wrong.");
}