Add config
This commit is contained in:
parent
32596228fe
commit
997f374fe4
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
build/
|
||||
web/
|
||||
.sessions/
|
||||
data/
|
||||
|
||||
*.swp
|
||||
*.swo
|
||||
|
|
|
@ -22,6 +22,7 @@ version "0.1.0"
|
|||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url "https://dl.bintray.com/kotlin/ktor" }
|
||||
maven { url "https://dl.bintray.com/jetbrains/markdown" }
|
||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||
|
@ -36,6 +37,7 @@ kotlin {
|
|||
jvm() {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs += [
|
||||
"-Xuse-experimental=io.ktor.util.KtorExperimentalAPI"
|
||||
]
|
||||
|
@ -88,6 +90,7 @@ kotlin {
|
|||
|
||||
implementation "de.westermann:KObserve-jvm:$observable_version"
|
||||
|
||||
implementation 'com.github.uchuhimo:konf:master-SNAPSHOT'
|
||||
implementation 'com.vladsch.flexmark:flexmark-all:0.42.10'
|
||||
api 'io.github.microutils:kotlin-logging:1.6.23'
|
||||
api 'ch.qos.logback:logback-classic:1.2.3'
|
||||
|
@ -167,6 +170,7 @@ task run(type: JavaExec, dependsOn: [jvmMainClasses, jsJar]) {
|
|||
clean.doFirst {
|
||||
delete webFolder
|
||||
delete ".sessions"
|
||||
delete "data"
|
||||
}
|
||||
|
||||
task jar(type: ShadowJar, dependsOn: [jvmMainClasses, jsMainClasses, sass]) {
|
||||
|
|
6
portal.toml
Normal file
6
portal.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[server]
|
||||
host = "localhost"
|
||||
port = 8080
|
||||
|
||||
[schedule]
|
||||
reference = "2019-06-12"
|
|
@ -9,7 +9,8 @@ data class Post(
|
|||
override val id: Long? = null,
|
||||
val name: String,
|
||||
val content: String,
|
||||
val url: String
|
||||
val url: String,
|
||||
val pinned: Boolean = false
|
||||
) : Model {
|
||||
|
||||
override fun createSearch() = SearchElement(
|
||||
|
|
|
@ -3,11 +3,28 @@ package de.kif.frontend.views.overview
|
|||
import de.kif.frontend.iterator
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.PostRepository
|
||||
import de.westermann.kobserve.event.subscribe
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.HTMLTextAreaElement
|
||||
import org.w3c.dom.events.EventListener
|
||||
import org.w3c.dom.get
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.clear
|
||||
|
||||
private fun sortOverviewPosts(container: HTMLElement) {
|
||||
val list = container.children.iterator().asSequence().toList()
|
||||
|
||||
val sorted = list.sortedWith(compareBy(
|
||||
{ if (it.dataset["pinned"]?.toBoolean() == true) 0 else 1 },
|
||||
{ -(it.dataset["id"]?.toLong() ?: -1) }
|
||||
))
|
||||
|
||||
container.clear()
|
||||
|
||||
for (element in sorted) {
|
||||
container.appendChild(element)
|
||||
}
|
||||
}
|
||||
|
||||
fun initOverviewMain() {
|
||||
val main = document.getElementsByClassName("overview-main")[0] as HTMLElement
|
||||
|
@ -15,24 +32,13 @@ fun initOverviewMain() {
|
|||
PostRepository.onCreate {
|
||||
val post = PostView.create(it)
|
||||
post.classList += "overview-post"
|
||||
|
||||
val first = main.firstElementChild as? HTMLElement
|
||||
|
||||
if (first == null) {
|
||||
main.appendChild(post.html)
|
||||
return@onCreate
|
||||
}
|
||||
if (first.classList.contains("post")) {
|
||||
main.insertBefore(post.html, first)
|
||||
return@onCreate
|
||||
|
||||
sortOverviewPosts(main)
|
||||
}
|
||||
|
||||
val next = first.nextElementSibling as? HTMLElement
|
||||
if (next == null) {
|
||||
main.appendChild(post.html)
|
||||
} else {
|
||||
main.insertBefore(post.html, next)
|
||||
}
|
||||
subscribe<PostChangeEvent> {
|
||||
sortOverviewPosts(main)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package de.kif.frontend.views.overview
|
||||
|
||||
import de.kif.common.model.Post
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.PostRepository
|
||||
import de.westermann.kobserve.event.emit
|
||||
import de.westermann.kwebview.View
|
||||
import de.westermann.kwebview.components.Link
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
|
@ -16,7 +16,13 @@ class PostView(
|
|||
view: HTMLElement
|
||||
) : View(view) {
|
||||
|
||||
private var postId = dataset["id"]?.toLongOrNull() ?: -1
|
||||
val postId = dataset["id"]?.toLongOrNull() ?: -1
|
||||
|
||||
var pinned: Boolean
|
||||
get() = dataset["pinned"] == "true"
|
||||
set(value) {
|
||||
dataset["pinned"] = value.toString()
|
||||
}
|
||||
|
||||
private val nameView: Link
|
||||
private val contentView: View
|
||||
|
@ -27,8 +33,11 @@ class PostView(
|
|||
|
||||
nameView.text = p.name
|
||||
nameView.target = "/p/${p.url}"
|
||||
pinned = p.pinned
|
||||
|
||||
contentView.html.innerHTML = PostRepository.htmlByUrl(p.url)
|
||||
|
||||
emit(PostChangeEvent(postId))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,3 +76,5 @@ class PostView(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class PostChangeEvent(val id: Long)
|
||||
|
|
|
@ -967,12 +967,6 @@ form {
|
|||
min-width: 20%;
|
||||
}
|
||||
|
||||
.overview-shortcuts {
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.overview-twitter {
|
||||
height: 20rem;
|
||||
background-color: #b3e6f9;
|
||||
|
|
|
@ -23,7 +23,7 @@ fun Application.main() {
|
|||
|
||||
install(ContentNegotiation) {
|
||||
jackson {
|
||||
enable(SerializationFeature.INDENT_OUTPUT) // Pretty Prints the JSON
|
||||
enable(SerializationFeature.INDENT_OUTPUT)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ fun Application.main() {
|
|||
|
||||
routing {
|
||||
static("/static") {
|
||||
files(Resources.directory)
|
||||
files(Configuration.Path.webPath.toFile())
|
||||
}
|
||||
|
||||
// UI routes
|
||||
|
|
89
src/jvmMain/kotlin/de/kif/backend/Configuration.kt
Normal file
89
src/jvmMain/kotlin/de/kif/backend/Configuration.kt
Normal file
|
@ -0,0 +1,89 @@
|
|||
package de.kif.backend
|
||||
|
||||
import com.uchuhimo.konf.Config
|
||||
import com.uchuhimo.konf.ConfigSpec
|
||||
import com.uchuhimo.konf.Item
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.Paths
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
object Configuration {
|
||||
|
||||
private val config: Config
|
||||
|
||||
class ConfigDelegate<T>(val item: Item<T>) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return config[item]
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> c(item: Item<T>) = ConfigDelegate(item)
|
||||
|
||||
private object ServerSpec : ConfigSpec("server") {
|
||||
val host by required<String>()
|
||||
val port by required<Int>()
|
||||
}
|
||||
|
||||
object Server {
|
||||
val host by c(ServerSpec.host)
|
||||
val port by c(ServerSpec.port)
|
||||
}
|
||||
|
||||
private object PathSpec : ConfigSpec("path") {
|
||||
val web by required<String>()
|
||||
val sessions by required<String>()
|
||||
val uploads by required<String>()
|
||||
val database by required<String>()
|
||||
}
|
||||
|
||||
object Path {
|
||||
val web by c(PathSpec.web)
|
||||
val webPath: java.nio.file.Path by lazy { Paths.get(web).toAbsolutePath() }
|
||||
|
||||
val sessions by c(PathSpec.sessions)
|
||||
val sessionsPath: java.nio.file.Path by lazy { Paths.get(sessions).toAbsolutePath() }
|
||||
|
||||
val uploads by c(PathSpec.uploads)
|
||||
val uploadsPath: java.nio.file.Path by lazy { Paths.get(uploads).toAbsolutePath() }
|
||||
|
||||
val database by c(PathSpec.database)
|
||||
val databasePath: java.nio.file.Path by lazy { Paths.get(database).toAbsolutePath() }
|
||||
}
|
||||
|
||||
private object ScheduleSpec : ConfigSpec("schedule") {
|
||||
val reference by required<String>()
|
||||
}
|
||||
|
||||
object Schedule {
|
||||
val reference by c(ScheduleSpec.reference)
|
||||
val referenceDate: Date by lazy { SimpleDateFormat("yyyy-MM-dd").parse(reference) }
|
||||
}
|
||||
|
||||
private object SecuritySpec : ConfigSpec("security") {
|
||||
val sessionName by required<String>("session_name")
|
||||
val signKey by required<String>("sign_key")
|
||||
}
|
||||
|
||||
object Security {
|
||||
val sessionName by c(SecuritySpec.sessionName)
|
||||
val signKey by c(SecuritySpec.signKey)
|
||||
}
|
||||
|
||||
init {
|
||||
var config = Config {
|
||||
addSpec(ServerSpec)
|
||||
addSpec(PathSpec)
|
||||
addSpec(ScheduleSpec)
|
||||
addSpec(SecuritySpec)
|
||||
}.from.toml.resource("portal.toml")
|
||||
|
||||
try {
|
||||
config = config.from.toml.file("portal.toml")
|
||||
} catch (_: FileNotFoundException) { }
|
||||
|
||||
this.config = config.from.env()
|
||||
.from.systemProperties()
|
||||
}
|
||||
}
|
|
@ -8,11 +8,14 @@ import io.ktor.application.Application
|
|||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.nio.file.Files
|
||||
|
||||
object Main {
|
||||
@Suppress("UnusedMainParameter")
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
Resources.init()
|
||||
|
||||
Connection.init()
|
||||
|
||||
runBlocking {
|
||||
|
@ -47,8 +50,8 @@ object Main {
|
|||
|
||||
embeddedServer(
|
||||
factory = Netty,
|
||||
port = 8080,
|
||||
host = "0.0.0.0",
|
||||
port = Configuration.Server.port,
|
||||
host = Configuration.Server.host,
|
||||
module = Application::main
|
||||
).start(wait = true)
|
||||
}
|
||||
|
|
|
@ -17,11 +17,17 @@ object Resources {
|
|||
*
|
||||
* @return File that points the extracted web directory.
|
||||
*/
|
||||
private fun extractWebFolder(): File {
|
||||
val destination: Path = Files.createTempDirectory("web")
|
||||
private fun extractWeb() {
|
||||
val destination: Path = Configuration.Path.webPath
|
||||
|
||||
val classPath = "web"
|
||||
val uri: URI = this::class.java.classLoader.getResource(classPath).toURI()
|
||||
val uri = this::class.java.classLoader.getResource(classPath)?.toURI()
|
||||
|
||||
if (uri == null) {
|
||||
logger.warn { "Cannot extract web content" }
|
||||
return
|
||||
}
|
||||
|
||||
val fileSystem: FileSystem?
|
||||
val src: Path = if (uri.scheme == "jar") {
|
||||
fileSystem = FileSystems.newFileSystem(uri, mutableMapOf<String, Any?>())
|
||||
|
@ -43,31 +49,33 @@ object Resources {
|
|||
}
|
||||
}
|
||||
|
||||
return destination.toFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* File that points the web directory.
|
||||
*/
|
||||
val directory = File(".").absoluteFile.canonicalFile.let { currentDir ->
|
||||
listOf(
|
||||
"web",
|
||||
"../web"
|
||||
).map {
|
||||
File(currentDir, it)
|
||||
}.firstOrNull { it.isDirectory }?.absoluteFile?.canonicalFile ?: extractWebFolder()
|
||||
}.also {
|
||||
logger.info { "Web directory: $it" }
|
||||
logger.info { "Successfully extract web content" }
|
||||
}
|
||||
|
||||
/**
|
||||
* List of js modules to be included.
|
||||
*/
|
||||
val jsModules = directory.list().toList().filter {
|
||||
val jsModules by lazy {
|
||||
Configuration.Path.webPath.toFile().list().toList().filter {
|
||||
it.endsWith(".js") &&
|
||||
!it.endsWith(".meta.js") &&
|
||||
!it.endsWith("-test.js") &&
|
||||
it != "require.min.js"
|
||||
}.joinToString(", ") { "'${it.take(it.length - 3)}'" }
|
||||
|
||||
}
|
||||
|
||||
fun init() {
|
||||
Files.createDirectories(Configuration.Path.databasePath.parent)
|
||||
Files.createDirectories(Configuration.Path.sessionsPath)
|
||||
Files.createDirectories(Configuration.Path.uploadsPath)
|
||||
Files.createDirectories(Configuration.Path.webPath)
|
||||
|
||||
logger.info { "Database path: ${Configuration.Path.databasePath}" }
|
||||
logger.info { "Sessions path: ${Configuration.Path.sessionsPath}" }
|
||||
logger.info { "Uploads path: ${Configuration.Path.uploadsPath}" }
|
||||
logger.info { "Web path: ${Configuration.Path.webPath}" }
|
||||
|
||||
logger.info { "Extract web content..." }
|
||||
extractWeb()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import io.ktor.sessions.*
|
|||
import io.ktor.util.hex
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
import java.io.File
|
||||
|
||||
interface ErrorContext {
|
||||
suspend infix fun onFailure(block: suspend () -> Unit)
|
||||
|
@ -103,20 +102,12 @@ fun Application.security() {
|
|||
}
|
||||
}
|
||||
|
||||
val encryptionKey =
|
||||
hex("80 51 b8 13 b4 73 a9 69 c7 b0 10 ad 08 06 11 e3".replace(" ", ""))
|
||||
val signKey =
|
||||
hex(
|
||||
"d1 20 23 8c 01 f8 f0 0d 9d 7c ff 68 21 97 75 31 38 3f fb 91 20 3a 8d 86 d4 e9 d8 50 f8 71 f1 dc".replace(
|
||||
" ",
|
||||
""
|
||||
)
|
||||
)
|
||||
val signKey = hex(Configuration.Security.signKey.replace(" ", ""))
|
||||
|
||||
install(Sessions) {
|
||||
cookie<PortalSession>(
|
||||
"SESSION",
|
||||
directorySessionStorage(File(".sessions"), cached = false)
|
||||
Configuration.Security.sessionName,
|
||||
directorySessionStorage(Configuration.Path.sessionsPath.toFile(), cached = false)
|
||||
) {
|
||||
cookie.path = "/"
|
||||
transform(SessionTransportTransformerMessageAuthentication(signKey))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package de.kif.backend.database
|
||||
|
||||
import de.kif.backend.Configuration
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
|
@ -11,7 +12,8 @@ import java.sql.Connection.TRANSACTION_SERIALIZABLE
|
|||
|
||||
object Connection {
|
||||
fun init() {
|
||||
Database.connect("jdbc:sqlite:portal.db", "org.sqlite.JDBC")
|
||||
val dbPath = Configuration.Path.databasePath.toString()
|
||||
Database.connect("jdbc:sqlite:$dbPath", "org.sqlite.JDBC")
|
||||
TransactionManager.manager.defaultIsolationLevel = TRANSACTION_SERIALIZABLE
|
||||
|
||||
transaction {
|
||||
|
|
|
@ -68,4 +68,5 @@ object DbPost : Table() {
|
|||
|
||||
val content = text("content")
|
||||
val url = varchar("url", 64).uniqueIndex()
|
||||
val pinned = bool("pinned")
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ object PostRepository : Repository<Post> {
|
|||
val name = row[DbPost.name]
|
||||
val content = row[DbPost.content]
|
||||
val url = row[DbPost.url]
|
||||
val pinned = row[DbPost.pinned]
|
||||
|
||||
return Post(id, name, content, url)
|
||||
return Post(id, name, content, url, pinned)
|
||||
}
|
||||
|
||||
override suspend fun get(id: Long): Post? {
|
||||
|
@ -33,11 +34,24 @@ object PostRepository : Repository<Post> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: Post): Long {
|
||||
if (model.pinned) {
|
||||
for (it in getPinned()) {
|
||||
update(it.copy(pinned = false))
|
||||
}
|
||||
}
|
||||
|
||||
return dbQuery {
|
||||
if (model.pinned) {
|
||||
DbPost.update({ DbPost.pinned eq true }) {
|
||||
it[pinned] = false
|
||||
}
|
||||
}
|
||||
|
||||
val id = DbPost.insert {
|
||||
it[name] = model.name
|
||||
it[content] = model.content
|
||||
it[url] = model.url
|
||||
it[pinned] = model.pinned
|
||||
}[DbPost.id] ?: throw IllegalStateException("Cannot create model!")
|
||||
|
||||
onCreate.emit(id)
|
||||
|
@ -48,11 +62,21 @@ object PostRepository : Repository<Post> {
|
|||
|
||||
override suspend fun update(model: Post) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
|
||||
if (model.pinned) {
|
||||
for (it in getPinned()) {
|
||||
if (it.id != model.id) {
|
||||
update(it.copy(pinned = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbQuery {
|
||||
DbPost.update({ DbPost.id eq model.id }) {
|
||||
it[name] = model.name
|
||||
it[content] = model.content
|
||||
it[url] = model.url
|
||||
it[pinned] = model.pinned
|
||||
}
|
||||
|
||||
onUpdate.emit(model.id)
|
||||
|
@ -69,7 +93,10 @@ object PostRepository : Repository<Post> {
|
|||
|
||||
override suspend fun all(): List<Post> {
|
||||
return dbQuery {
|
||||
val result = DbPost.selectAll()
|
||||
val result = DbPost.selectAll().orderBy(
|
||||
DbPost.pinned to SortOrder.ASC,
|
||||
DbPost.id to SortOrder.ASC
|
||||
)
|
||||
|
||||
result.map(this::rowToModel)
|
||||
}
|
||||
|
@ -79,12 +106,18 @@ object PostRepository : Repository<Post> {
|
|||
return dbQuery {
|
||||
val result = DbPost.select { DbPost.url eq url }
|
||||
|
||||
runBlocking {
|
||||
result.firstOrNull()?.let {
|
||||
rowToModel(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getPinned(): List<Post> {
|
||||
return dbQuery {
|
||||
val result = DbPost.select { DbPost.pinned eq true }
|
||||
|
||||
result.map(this::rowToModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun registerPushService() {
|
||||
|
|
|
@ -27,6 +27,8 @@ fun DIV.createPost(post: Post, editable: Boolean = false, additionalClasses: Str
|
|||
}
|
||||
div(classes) {
|
||||
attributes["data-id"] = post.id.toString()
|
||||
attributes["data-pinned"] = post.pinned.toString()
|
||||
|
||||
a("/p/${post.url}", classes = "post-name") {
|
||||
+post.name
|
||||
}
|
||||
|
@ -58,37 +60,16 @@ fun Route.overview() {
|
|||
content {
|
||||
div("overview") {
|
||||
div("overview-main") {
|
||||
if (editable) {
|
||||
div("overview-new") {
|
||||
a("post/new", classes = "form-btn") {
|
||||
+"New"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (post in postList) {
|
||||
createPost(post, editable, "overview-post")
|
||||
}
|
||||
}
|
||||
div("overview-side") {
|
||||
div("overview-shortcuts") {
|
||||
a {
|
||||
+"Wiki"
|
||||
if (editable) {
|
||||
div("overview-new") {
|
||||
a("post/new", classes = "form-btn") {
|
||||
+"New"
|
||||
}
|
||||
a {
|
||||
+"Wiki"
|
||||
}
|
||||
a {
|
||||
+"Wiki"
|
||||
}
|
||||
a {
|
||||
+"Wiki"
|
||||
}
|
||||
a {
|
||||
+"Wiki"
|
||||
}
|
||||
a {
|
||||
+"Wiki"
|
||||
}
|
||||
}
|
||||
div("overview-twitter") {
|
||||
|
@ -124,7 +105,6 @@ fun Route.overview() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
get("/post/{id}") {
|
||||
authenticateOrRedirect(Permission.POST) { user ->
|
||||
val postId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||
|
@ -153,6 +133,7 @@ fun Route.overview() {
|
|||
value = editPost.name
|
||||
}
|
||||
}
|
||||
|
||||
div("form-group") {
|
||||
label {
|
||||
htmlFor = "url"
|
||||
|
@ -162,12 +143,29 @@ fun Route.overview() {
|
|||
name = "url",
|
||||
classes = "form-control"
|
||||
) {
|
||||
id = "places"
|
||||
placeholder = "Places"
|
||||
id = "url"
|
||||
placeholder = "Url"
|
||||
value = editPost.url
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "pinned",
|
||||
classes = "form-control",
|
||||
type = InputType.checkBox
|
||||
) {
|
||||
id = "pinned"
|
||||
checked = editPost.pinned
|
||||
}
|
||||
label {
|
||||
htmlFor = "pinned"
|
||||
+"Pinned"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-group") {
|
||||
label {
|
||||
htmlFor = "content"
|
||||
|
@ -218,6 +216,7 @@ fun Route.overview() {
|
|||
params["name"]?.let { post = post.copy(name = it) }
|
||||
params["url"]?.let { post = post.copy(url = it) }
|
||||
params["content"]?.let { post = post.copy(content = it) }
|
||||
params["pinned"]?.let { post = post.copy(pinned = it == "on") }
|
||||
|
||||
PostRepository.update(post)
|
||||
|
||||
|
@ -260,12 +259,29 @@ fun Route.overview() {
|
|||
name = "url",
|
||||
classes = "form-control"
|
||||
) {
|
||||
id = "places"
|
||||
placeholder = "Places"
|
||||
id = "url"
|
||||
placeholder = "Url"
|
||||
value = Post.generateUrl()
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "pinned",
|
||||
classes = "form-control",
|
||||
type = InputType.checkBox
|
||||
) {
|
||||
id = "pinned"
|
||||
checked = false
|
||||
}
|
||||
label {
|
||||
htmlFor = "pinned"
|
||||
+"Pinned"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-group") {
|
||||
label {
|
||||
htmlFor = "content"
|
||||
|
@ -309,8 +325,9 @@ fun Route.overview() {
|
|||
val name = params["name"] ?: return@post
|
||||
val content = params["content"] ?: return@post
|
||||
val url = params["url"] ?: return@post
|
||||
val pinned = params["pinned"] == "on"
|
||||
|
||||
val post = Post(null, name, content, url)
|
||||
val post = Post(null, name, content, url, pinned)
|
||||
|
||||
PostRepository.create(post)
|
||||
|
||||
|
|
|
@ -166,9 +166,7 @@ fun Route.room() {
|
|||
+"Projector"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "internet",
|
||||
|
@ -183,9 +181,7 @@ fun Route.room() {
|
|||
+"Internet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "whiteboard",
|
||||
|
@ -200,9 +196,7 @@ fun Route.room() {
|
|||
+"Whiteboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "blackboard",
|
||||
|
@ -217,9 +211,7 @@ fun Route.room() {
|
|||
+"Blackboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "accessible",
|
||||
|
@ -337,9 +329,7 @@ fun Route.room() {
|
|||
+"Projector"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "internet",
|
||||
|
@ -354,9 +344,7 @@ fun Route.room() {
|
|||
+"Internet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "whiteboard",
|
||||
|
@ -371,9 +359,7 @@ fun Route.room() {
|
|||
+"Whiteboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "blackboard",
|
||||
|
@ -388,9 +374,7 @@ fun Route.room() {
|
|||
+"Blackboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div("form-switch-group") {
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "accessible",
|
||||
|
|
16
src/jvmMain/resources/portal.toml
Normal file
16
src/jvmMain/resources/portal.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[server]
|
||||
host = "localhost"
|
||||
port = 8080
|
||||
|
||||
[path]
|
||||
web = "web"
|
||||
sessions = "data/sessions"
|
||||
uploads = "data/uploads"
|
||||
database = "data/portal.db"
|
||||
|
||||
[schedule]
|
||||
reference = "2019-03-27"
|
||||
|
||||
[security]
|
||||
session_name = "SESSION"
|
||||
sign_key = "d1 20 23 8c 01 f8 f0 0d 9d 7c ff 68 21 97 75 31 38 3f fb 91 20 3a 8d 86 d4 e9 d8 50 f8 71 f1 dc"
|
Loading…
Reference in a new issue