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 modifiers = TemplateModifiers()
|
||||||
|
|
||||||
private val loginPageController = LoginController(modifiers, repository)
|
private val loginPageController = AccountController(modifiers, repository)
|
||||||
|
|
||||||
private val overviewController = OverviewController(modifiers, repository)
|
private val overviewController = OverviewController(modifiers, repository)
|
||||||
private val limboController = LimboController(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("/login", loginPageController::doLogin, Role.ANON, Role.USER)
|
||||||
server.post("/logout", loginPageController::doLogout, 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.post("/upload", uploadController::upload, Role.USER)
|
||||||
server.get("/limbo", limboController::formLimbo, Role.USER)
|
server.get("/limbo", limboController::formLimbo, Role.USER)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,13 @@ package net.h34t.filemure
|
||||||
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
data class AccountRef(
|
||||||
|
val id: Long,
|
||||||
|
val extId: ExtId,
|
||||||
|
val email: String,
|
||||||
|
val created: LocalDateTime,
|
||||||
|
val state: State
|
||||||
|
)
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class ExtId(val value: String) {
|
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.ColumnAdapter
|
||||||
import app.cash.sqldelight.db.SqlDriver
|
import app.cash.sqldelight.db.SqlDriver
|
||||||
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
|
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
|
||||||
|
import at.favre.lib.crypto.bcrypt.BCrypt
|
||||||
import net.h34t.filemure.*
|
import net.h34t.filemure.*
|
||||||
import net.h34t.filemure.TagAdapter.serialize
|
import net.h34t.filemure.TagAdapter.serialize
|
||||||
|
import net.h34t.filemure.db.Account
|
||||||
import net.h34t.filemure.db.Database
|
import net.h34t.filemure.db.Database
|
||||||
import net.h34t.filemure.db.File_
|
import net.h34t.filemure.db.File_
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
@ -54,6 +56,11 @@ class SqliteRepository(url: String) {
|
||||||
|
|
||||||
database = Database(
|
database = Database(
|
||||||
driver = driver,
|
driver = driver,
|
||||||
|
accountAdapter = Account.Adapter(
|
||||||
|
ext_idAdapter = extIdAdapter,
|
||||||
|
createdAdapter = localDateTimeAdapter,
|
||||||
|
stateAdapter = stateAdapter
|
||||||
|
),
|
||||||
documentAdapter = net.h34t.filemure.db.Document.Adapter(
|
documentAdapter = net.h34t.filemure.db.Document.Adapter(
|
||||||
tagsAdapter = tagsAdapter,
|
tagsAdapter = tagsAdapter,
|
||||||
createdAdapter = localDateTimeAdapter,
|
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(
|
fun addFileToLimbo(
|
||||||
accountId: Long,
|
accountId: Long,
|
||||||
filename: String,
|
filename: String,
|
||||||
|
@ -77,7 +125,7 @@ class SqliteRepository(url: String) {
|
||||||
content: InputStream
|
content: InputStream
|
||||||
): IdPair =
|
): IdPair =
|
||||||
database.databaseQueries.transactionWithResult {
|
database.databaseQueries.transactionWithResult {
|
||||||
val extId = ExtId.generate()
|
val extId = generateExtId()
|
||||||
database.databaseQueries.insertFileIntoLimbo(
|
database.databaseQueries.insertFileIntoLimbo(
|
||||||
account_id = accountId,
|
account_id = accountId,
|
||||||
ext_id = extId,
|
ext_id = extId,
|
||||||
|
@ -102,7 +150,7 @@ class SqliteRepository(url: String) {
|
||||||
content: InputStream
|
content: InputStream
|
||||||
) =
|
) =
|
||||||
database.databaseQueries.transactionWithResult {
|
database.databaseQueries.transactionWithResult {
|
||||||
ExtId.generate().let { extId ->
|
generateExtId().let { extId ->
|
||||||
database.databaseQueries.insertFileForDocument(
|
database.databaseQueries.insertFileForDocument(
|
||||||
account_id = accountId,
|
account_id = accountId,
|
||||||
document_id = documentId,
|
document_id = documentId,
|
||||||
|
@ -148,7 +196,7 @@ class SqliteRepository(url: String) {
|
||||||
description: String,
|
description: String,
|
||||||
fileExtIds: List<ExtId>
|
fileExtIds: List<ExtId>
|
||||||
): ExtId {
|
): ExtId {
|
||||||
val extId = ExtId.generate()
|
val extId = generateExtId()
|
||||||
database.databaseQueries.transaction {
|
database.databaseQueries.transaction {
|
||||||
database.databaseQueries.addDocument(
|
database.databaseQueries.addDocument(
|
||||||
account_id = accountId,
|
account_id = accountId,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import net.h34t.filemure.Tag;
|
||||||
|
|
||||||
CREATE TABLE account (
|
CREATE TABLE account (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
ext_id TEXT AS ExtId NOT NULL,
|
ext_id TEXT AS ExtId NOT NULL,
|
||||||
email TEXT NOT NULL,
|
email TEXT NOT NULL,
|
||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
created TEXT AS LocalDateTime DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
|
created TEXT AS LocalDateTime DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
|
||||||
|
@ -59,6 +59,12 @@ CREATE TABLE file (
|
||||||
CREATE INDEX file_state_IDX ON file (state);
|
CREATE INDEX file_state_IDX ON file (state);
|
||||||
CREATE UNIQUE INDEX file_ext_id_IDX ON file (ext_id);
|
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:
|
insertFileForDocument:
|
||||||
INSERT INTO file (account_id, document_id, ext_id, filename, content_type, file_size, content) VALUES (?, ?,?,?,?,?,?);
|
INSERT INTO file (account_id, document_id, ext_id, filename, content_type, file_size, content) VALUES (?, ?,?,?,?,?,?);
|
||||||
|
|
|
@ -30,7 +30,13 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<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>
|
<button class="small"><i>delete</i><span>delete</span></button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
<h1>Hello to Filemure</h1>
|
|
||||||
|
|
||||||
<form action="/login" method="post">
|
<form action="/login" method="post">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Log In</legend>
|
<legend>Log In</legend>
|
||||||
|
|
||||||
|
@ -21,6 +18,11 @@
|
||||||
</button>
|
</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>
|
</fieldset>
|
||||||
</form>
|
</form>
|
|
@ -10,7 +10,9 @@
|
||||||
Files in <a href="/limbo">limbo: {$limboFileCount}</a>
|
Files in <a href="/limbo">limbo: {$limboFileCount}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="min">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -40,27 +42,25 @@
|
||||||
<table class="stripes">
|
<table class="stripes">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date</th>
|
<th class="min">Date</th>
|
||||||
<th>Files</th>
|
<th class="max">Files</th>
|
||||||
<th>View</th>
|
<th class="right-align">View</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{for $year}
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="3">{*$year}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{for $month}
|
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{for $year}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{*$monthHuman}</td>
|
<th colspan="3"><h6>{*$year}</h6></th>
|
||||||
<td>{*$count}</td>
|
</tr>
|
||||||
<td><a href="/overview/{*$year}/{*$month}">
|
{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>
|
<button class="small"><span>view</span></button>
|
||||||
</a></td>
|
</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{/for}
|
||||||
|
{/for}
|
||||||
</tbody>
|
</tbody>
|
||||||
{/for}
|
|
||||||
{/for}
|
|
||||||
</table>
|
</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