Adds registration.
This commit is contained in:
parent
52e4568fcc
commit
5356f47c9c
10 changed files with 205 additions and 67 deletions
|
@ -11,7 +11,7 @@ class FilemureApp(repository: SqliteRepository) {
|
|||
|
||||
private val modifiers = TemplateModifiers()
|
||||
|
||||
private val loginPageController = LoginController(modifiers, repository)
|
||||
private val loginPageController = AccountController(modifiers, repository)
|
||||
|
||||
private val overviewController = OverviewController(modifiers, repository)
|
||||
private val limboController = LimboController(modifiers, repository)
|
||||
|
@ -33,6 +33,9 @@ class FilemureApp(repository: SqliteRepository) {
|
|||
server.post("/login", loginPageController::doLogin, Role.ANON, Role.USER)
|
||||
server.post("/logout", loginPageController::doLogout, Role.USER)
|
||||
|
||||
server.get("/register", loginPageController::formRegister, Role.ANON, Role.USER)
|
||||
server.post("/register", loginPageController::doRegister, Role.ANON, Role.USER)
|
||||
|
||||
server.post("/upload", uploadController::upload, Role.USER)
|
||||
server.get("/limbo", limboController::formLimbo, Role.USER)
|
||||
|
||||
|
|
|
@ -2,6 +2,13 @@ package net.h34t.filemure
|
|||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
data class AccountRef(
|
||||
val id: Long,
|
||||
val extId: ExtId,
|
||||
val email: String,
|
||||
val created: LocalDateTime,
|
||||
val state: State
|
||||
)
|
||||
|
||||
@JvmInline
|
||||
value class ExtId(val value: String) {
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package net.h34t.filemure.controller
|
||||
|
||||
import io.javalin.http.BadRequestResponse
|
||||
import io.javalin.http.Context
|
||||
import net.h34t.filemure.*
|
||||
import net.h34t.filemure.repository.SqliteRepository
|
||||
import net.h34t.filemure.tpl.Frame
|
||||
import net.h34t.filemure.tpl.Login
|
||||
import net.h34t.filemure.tpl.Register
|
||||
|
||||
class AccountController(private val modifiers: TemplateModifiers, private val repository: SqliteRepository) {
|
||||
|
||||
fun formLogin(ctx: Context) {
|
||||
val loginError = ctx.queryParam("login_error") != null
|
||||
|
||||
ctx.tempolin(
|
||||
Frame(
|
||||
modifiers = modifiers,
|
||||
title = "Login",
|
||||
target = "",
|
||||
back = "",
|
||||
logout = false,
|
||||
content = Login(
|
||||
loginError = loginError
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun doLogin(ctx: Context) {
|
||||
val username = ctx.formParam("username") ?: throw BadRequestResponse()
|
||||
val password = ctx.formParam("password") ?: throw BadRequestResponse()
|
||||
|
||||
val acc = repository.loginAccount(email = username, password = password.trim().toCharArray())
|
||||
|
||||
if (acc != null) {
|
||||
ctx.setSession(Session(id = acc.id, email = acc.email))
|
||||
ctx.redirectPRG("/")
|
||||
} else {
|
||||
ctx.setSession(null)
|
||||
ctx.redirectPRG("/login/?login_error")
|
||||
}
|
||||
}
|
||||
|
||||
fun doLogout(ctx: Context) {
|
||||
ctx.setSession(null)
|
||||
ctx.redirectPRG("/login")
|
||||
}
|
||||
|
||||
fun formRegister(ctx: Context) {
|
||||
val registerError = ctx.queryParam("register_error") != null
|
||||
|
||||
ctx.tempolin(
|
||||
Frame(
|
||||
modifiers = modifiers,
|
||||
title = "Register",
|
||||
target = "",
|
||||
back = "/",
|
||||
logout = false,
|
||||
content = Register(
|
||||
registerError = registerError
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun doRegister(ctx: Context) {
|
||||
val username = ctx.formParam("username") ?: throw BadRequestResponse()
|
||||
val password = ctx.formParam("password") ?: throw BadRequestResponse()
|
||||
|
||||
try {
|
||||
require(username.isNotBlank())
|
||||
require(password.isNotBlank() && password.length >= 6)
|
||||
|
||||
repository.addAccount(email = username, password = password.trim().toCharArray())
|
||||
ctx.redirectPRG("/login")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
ctx.redirectPRG("/register?register_error")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package net.h34t.filemure.controller
|
||||
|
||||
import io.javalin.http.Context
|
||||
import net.h34t.filemure.*
|
||||
import net.h34t.filemure.repository.SqliteRepository
|
||||
import net.h34t.filemure.tpl.Frame
|
||||
import net.h34t.filemure.tpl.Login
|
||||
|
||||
class LoginController(val modifiers: TemplateModifiers, val repository: SqliteRepository) {
|
||||
|
||||
fun formLogin(ctx: Context) {
|
||||
ctx.tempolin(
|
||||
Frame(
|
||||
modifiers = modifiers,
|
||||
title = "Hello to Filemure",
|
||||
target = "",
|
||||
back = "",
|
||||
logout = false,
|
||||
content = Login()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun doLogin(ctx: Context) {
|
||||
val username = ctx.formParam("username")
|
||||
val password = ctx.formParam("password")
|
||||
|
||||
if (username == "stefan@schallerl.com" && password == "foobar") {
|
||||
ctx.setSession(Session(id = 1, email = username))
|
||||
ctx.redirectPRG("/")
|
||||
} else {
|
||||
ctx.setSession(null)
|
||||
ctx.redirectPRG("/")
|
||||
}
|
||||
}
|
||||
|
||||
fun doLogout(ctx: Context) {
|
||||
ctx.setSession(null)
|
||||
ctx.redirectPRG("/login")
|
||||
}
|
||||
}
|
|
@ -3,8 +3,10 @@ package net.h34t.filemure.repository
|
|||
import app.cash.sqldelight.ColumnAdapter
|
||||
import app.cash.sqldelight.db.SqlDriver
|
||||
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt
|
||||
import net.h34t.filemure.*
|
||||
import net.h34t.filemure.TagAdapter.serialize
|
||||
import net.h34t.filemure.db.Account
|
||||
import net.h34t.filemure.db.Database
|
||||
import net.h34t.filemure.db.File_
|
||||
import java.io.InputStream
|
||||
|
@ -54,6 +56,11 @@ class SqliteRepository(url: String) {
|
|||
|
||||
database = Database(
|
||||
driver = driver,
|
||||
accountAdapter = Account.Adapter(
|
||||
ext_idAdapter = extIdAdapter,
|
||||
createdAdapter = localDateTimeAdapter,
|
||||
stateAdapter = stateAdapter
|
||||
),
|
||||
documentAdapter = net.h34t.filemure.db.Document.Adapter(
|
||||
tagsAdapter = tagsAdapter,
|
||||
createdAdapter = localDateTimeAdapter,
|
||||
|
@ -69,6 +76,47 @@ class SqliteRepository(url: String) {
|
|||
)
|
||||
}
|
||||
|
||||
private val bcrypt = BCrypt.withDefaults()
|
||||
|
||||
private fun generateExtId(): ExtId = ExtId.generate()
|
||||
|
||||
fun addAccount(
|
||||
email: String,
|
||||
password: CharArray
|
||||
) {
|
||||
val bcryptPassword = bcrypt.hashToString(6, password)
|
||||
val extId = generateExtId()
|
||||
database.databaseQueries.addAccount(
|
||||
extId = extId,
|
||||
email = email,
|
||||
password = bcryptPassword,
|
||||
state = State.ACTIVE
|
||||
)
|
||||
}
|
||||
|
||||
fun loginAccount(email: String, password: CharArray): AccountRef? {
|
||||
return database.databaseQueries.loginAccount(email).executeAsList().firstOrNull()?.let { account ->
|
||||
if (account.state != State.ACTIVE)
|
||||
return null
|
||||
|
||||
val verify = BCrypt.verifyer().verify(password, account.password)
|
||||
|
||||
if (verify.verified) {
|
||||
account.let {
|
||||
AccountRef(
|
||||
id = it.id,
|
||||
extId = it.ext_id,
|
||||
email = it.email,
|
||||
created = it.created,
|
||||
state = it.state
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addFileToLimbo(
|
||||
accountId: Long,
|
||||
filename: String,
|
||||
|
@ -77,7 +125,7 @@ class SqliteRepository(url: String) {
|
|||
content: InputStream
|
||||
): IdPair =
|
||||
database.databaseQueries.transactionWithResult {
|
||||
val extId = ExtId.generate()
|
||||
val extId = generateExtId()
|
||||
database.databaseQueries.insertFileIntoLimbo(
|
||||
account_id = accountId,
|
||||
ext_id = extId,
|
||||
|
@ -102,7 +150,7 @@ class SqliteRepository(url: String) {
|
|||
content: InputStream
|
||||
) =
|
||||
database.databaseQueries.transactionWithResult {
|
||||
ExtId.generate().let { extId ->
|
||||
generateExtId().let { extId ->
|
||||
database.databaseQueries.insertFileForDocument(
|
||||
account_id = accountId,
|
||||
document_id = documentId,
|
||||
|
@ -148,7 +196,7 @@ class SqliteRepository(url: String) {
|
|||
description: String,
|
||||
fileExtIds: List<ExtId>
|
||||
): ExtId {
|
||||
val extId = ExtId.generate()
|
||||
val extId = generateExtId()
|
||||
database.databaseQueries.transaction {
|
||||
database.databaseQueries.addDocument(
|
||||
account_id = accountId,
|
||||
|
|
|
@ -59,6 +59,12 @@ CREATE TABLE file (
|
|||
CREATE INDEX file_state_IDX ON file (state);
|
||||
CREATE UNIQUE INDEX file_ext_id_IDX ON file (ext_id);
|
||||
|
||||
addAccount:
|
||||
INSERT INTO account (ext_id, email, password, state) VALUES (:extId, :email, :password, :state);
|
||||
|
||||
loginAccount:
|
||||
SELECT id, ext_id, email, password, created, state FROM account WHERE email=:email;
|
||||
|
||||
---
|
||||
insertFileForDocument:
|
||||
INSERT INTO file (account_id, document_id, ext_id, filename, content_type, file_size, content) VALUES (?, ?,?,?,?,?,?);
|
||||
|
|
|
@ -30,7 +30,13 @@
|
|||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/file/{*$extId}/delete?return=limbo" method="POST">
|
||||
<form action="/document/new" method="get">
|
||||
<input type="hidden" name="file_id" value="{*$extId}">
|
||||
<button class="small"><i>add</i><span>document</span></button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/file/{*$extId}/delete?return=limbo" method="post">
|
||||
<button class="small"><i>delete</i><span>delete</span></button>
|
||||
</form>
|
||||
</td>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<h1>Hello to Filemure</h1>
|
||||
|
||||
<form action="/login" method="post">
|
||||
|
||||
<fieldset>
|
||||
<legend>Log In</legend>
|
||||
|
||||
|
@ -21,6 +18,11 @@
|
|||
</button>
|
||||
|
||||
|
||||
<!--<p><input type="submit" value="login"></p>-->
|
||||
<p>Or <a href="/register">register a new account</a>.</p>
|
||||
|
||||
{if $loginError}
|
||||
<div class="snackbar error active">Email or password wrong or the account doesn't exist.</div>
|
||||
{/if}
|
||||
|
||||
</fieldset>
|
||||
</form>
|
|
@ -10,7 +10,9 @@
|
|||
Files in <a href="/limbo">limbo: {$limboFileCount}</a>
|
||||
</div>
|
||||
<div class="min">
|
||||
<a href="/limbo"><button><i>folder_open</i><span>view</span></button></a>
|
||||
<a href="/limbo">
|
||||
<button><i>folder_open</i><span>view</span></button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
@ -40,27 +42,25 @@
|
|||
<table class="stripes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Files</th>
|
||||
<th>View</th>
|
||||
<th class="min">Date</th>
|
||||
<th class="max">Files</th>
|
||||
<th class="right-align">View</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{for $year}
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3">{*$year}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{for $month}
|
||||
<tbody>
|
||||
{for $year}
|
||||
<tr>
|
||||
<td>{*$monthHuman}</td>
|
||||
<td>{*$count}</td>
|
||||
<td><a href="/overview/{*$year}/{*$month}">
|
||||
<th colspan="3"><h6>{*$year}</h6></th>
|
||||
</tr>
|
||||
{for $month}
|
||||
<tr>
|
||||
<td class="min">{*$monthHuman}</td>
|
||||
<td class="max">{*$count}</td>
|
||||
<td class="right-align"><a href="/overview/{*$year}/{*$month}">
|
||||
<button class="small"><span>view</span></button>
|
||||
</a></td>
|
||||
</tr>
|
||||
{/for}
|
||||
{/for}
|
||||
</tbody>
|
||||
{/for}
|
||||
{/for}
|
||||
</table>
|
||||
|
|
25
app/src/main/tpl/net.h34t.filemure.tpl/Register.tpl.html
Normal file
25
app/src/main/tpl/net.h34t.filemure.tpl/Register.tpl.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<form action="/register" method="post">
|
||||
<fieldset>
|
||||
<legend>Register</legend>
|
||||
|
||||
<div class="field border label">
|
||||
<input id="email" type="email" placeholder="your-email@example.com" name="username">
|
||||
<label for="email">Your email</label>
|
||||
</div>
|
||||
|
||||
<div class="field border label">
|
||||
<input id="password" type="password" name="password">
|
||||
<label for="password">Your password</label>
|
||||
</div>
|
||||
|
||||
<button>
|
||||
<i>add</i>
|
||||
<span>register</span>
|
||||
</button>
|
||||
|
||||
{if $registerError}
|
||||
<div class="snackbar error active">Email or password invalid or account already exists.</div>
|
||||
{/if}
|
||||
|
||||
</fieldset>
|
||||
</form>
|
Loading…
Reference in a new issue