18 Commits

Author SHA1 Message Date
hubert bfd562bc60 Add Gradle Wrapper 2020-10-26 02:10:22 +01:00
hubert 4fb85a52e4 Use Gradle ! 2020-10-26 02:10:22 +01:00
hubert e64352f54c Fix arrow depreciations 2020-10-25 23:46:56 +01:00
hubert c2c03e415e Move Logback.xml 2020-10-24 01:36:51 +02:00
hubert 0260bea951 Move ConfigLoader 2020-10-24 01:36:51 +02:00
hubert 8b8dbd6fe5 Separate views into a maven module 2020-10-24 01:36:51 +02:00
hubert 536c6e7b79 Merge branch 'jigsaw' into master 2020-10-24 00:05:09 +02:00
hubert e5a2b8993f Remove module-infos
Some libraires are not yet ready..

{Arrow,Lucene,Http4k} should be packed inside a shaded jar because they
all have some split packages

HikariCP has an invalid module-info.java

Kapt doesn't work
2020-10-23 23:20:58 +02:00
hubert 38750a588c Move packages + remove circular dependencies 2020-10-23 23:20:58 +02:00
hubert ee026ec829 Move junit config to simplenotes-test-resources 2020-10-23 16:36:44 +02:00
hubert 29b024d360 Move ArrowAssertions to simplenotes-domain 2020-10-23 16:30:43 +02:00
hubert cd12d1561a Move config into simplenotes-config module 2020-10-23 16:24:50 +02:00
hubert c2eaf3d0cc Move types into simplenotes-types module 2020-10-23 16:12:40 +02:00
hubert 4c9ac8944e Prefix maven modules 2020-10-23 15:45:28 +02:00
hubert 4ff97044f0 Change public/private button css 2020-10-23 08:26:03 +02:00
hubert ead1932d48 Fix some css 2020-10-23 07:16:07 +02:00
hubert 4a7dcec363 Use Json lenses 2020-10-23 06:22:44 +02:00
hubert cb76a3253d Use mapstruct 2020-10-21 22:55:36 +02:00
172 changed files with 1546 additions and 1381 deletions
+4 -88
View File
@@ -7,18 +7,9 @@
*.nar *.nar
hs_err_pid* hs_err_pid*
# Maven # Gradle
target/ build/
pom.xml.tag .gradle
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
pom.xml.bak
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
# IntelliJ # IntelliJ
out/ out/
@@ -28,11 +19,8 @@ out/
*.ipr *.ipr
*.iws *.iws
# Vue
node_modules
/dist
# Local env files # Local env files
.env
.env.local .env.local
.env.*.local .env.*.local
@@ -49,85 +37,13 @@ pids
*.seed *.seed
*.pid.lock *.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories # Dependency directories
node_modules/ node_modules/
jspm_packages/ jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file # Yarn Integrity file
.yarn-integrity .yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
*.private.env.json
# Certificates
data/
letsencrypt/
# generated resources
app/src/main/resources/css-manifest.json
app/src/main/resources/static/styles*
# h2 db # h2 db
*.db *.db
+2 -24
View File
@@ -1,25 +1,3 @@
FROM maven:3.6.3-jdk-14 as builder
WORKDIR /tmp
# Cache dependencies
COPY pom.xml .
COPY app/pom.xml app/pom.xml
COPY domain/pom.xml domain/pom.xml
COPY persistance/pom.xml persistance/pom.xml
COPY shared/pom.xml shared/pom.xml
COPY search/pom.xml search/pom.xml
RUN mvn verify clean --fail-never
COPY app/src app/src
COPY domain/src domain/src
COPY persistance/src persistance/src
COPY shared/src shared/src
COPY search/src search/src
RUN mvn -Dstyle.color=always package
FROM openjdk:14-alpine as jdkbuilder FROM openjdk:14-alpine as jdkbuilder
RUN apk add --no-cache binutils RUN apk add --no-cache binutils
@@ -42,8 +20,8 @@ RUN chown -R $APPLICATION_USER /app
USER $APPLICATION_USER USER $APPLICATION_USER
COPY --from=builder /tmp/app/target/app-*.jar /app/app.jar
COPY --from=jdkbuilder /myjdk /myjdk COPY --from=jdkbuilder /myjdk /myjdk
COPY simplenotes-app/build/libs/app-*-all.jar /app/simplenotes.jar
WORKDIR /app WORKDIR /app
CMD ["/myjdk/bin/java", "-server", "-XX:+UnlockExperimentalVMOptions", "-Xms64m", "-Xmx256m", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "app.jar"] CMD ["/myjdk/bin/java", "-server", "-XX:+UnlockExperimentalVMOptions", "-Xms64m", "-Xmx256m", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "simplenotes.jar"]
-191
View File
@@ -1,191 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>be.simplenotes</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>app</artifactId>
<properties>
<http4k.version>3.268.0</http4k.version>
</properties>
<dependencies>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>persistance</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>search</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>domain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.http4k</groupId>
<artifactId>http4k-core</artifactId>
</dependency>
<dependency>
<groupId>org.http4k</groupId>
<artifactId>http4k-server-jetty</artifactId>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-html-jvm</artifactId>
<version>0.7.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json-jvm</artifactId>
</dependency>
<dependency>
<groupId>org.ocpsoft.prettytime</groupId>
<artifactId>prettytime</artifactId>
<version>4.0.5.Final</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.http4k</groupId>
<artifactId>http4k-testing-hamkrest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>me.liuwj.ktorm</groupId>
<artifactId>ktorm-core</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.http4k</groupId>
<artifactId>http4k-bom</artifactId>
<version>${http4k.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>be.simplenotes.app.SimpleNotesKt</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>com.h2database:h2</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.mariadb.jdbc:mariadb-java-client</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.jetbrains.kotlin:kotlin-reflect</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.eclipse.jetty:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.apache.lucene:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.ocpsoft.prettytime:prettytime</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/maven/**</exclude>
<exclude>META-INF/proguard/**</exclude>
<exclude>META-INF/*.kotlin_module</exclude>
<exclude>META-INF/DEPENDENCIES*</exclude>
<exclude>META-INF/NOTICE*</exclude>
<exclude>META-INF/LICENSE*</exclude>
<exclude>LICENSE*</exclude>
<exclude>META-INF/README*</exclude>
<exclude>META-INF/native-image/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,77 +0,0 @@
package be.simplenotes.app.api
import be.simplenotes.app.extensions.json
import be.simplenotes.app.utils.parseSearchTerms
import be.simplenotes.domain.model.PersistedNote
import be.simplenotes.domain.model.PersistedNoteMetadata
import be.simplenotes.domain.security.JwtPayload
import be.simplenotes.domain.usecases.NoteService
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.BAD_REQUEST
import org.http4k.core.Status.Companion.NOT_FOUND
import org.http4k.core.Status.Companion.OK
import org.http4k.routing.path
import java.util.*
class ApiNoteController(private val noteService: NoteService, private val json: Json) {
fun createNote(request: Request, jwtPayload: JwtPayload): Response {
val content = json.decodeFromString(NoteContent.serializer(), request.bodyString()).content
return noteService.create(jwtPayload.userId, content).fold(
{
Response(BAD_REQUEST)
},
{
Response(OK).json(json.encodeToString(UuidContent.serializer(), UuidContent(it.uuid)))
}
)
}
fun notes(request: Request, jwtPayload: JwtPayload): Response {
val notes = noteService.paginatedNotes(jwtPayload.userId, page = 1).notes
val json = json.encodeToString(ListSerializer(PersistedNoteMetadata.serializer()), notes)
return Response(OK).json(json)
}
fun note(request: Request, jwtPayload: JwtPayload): Response {
val uuid = request.path("uuid")!!
return noteService.find(jwtPayload.userId, UUID.fromString(uuid))
?.let { Response(OK).json(json.encodeToString(PersistedNote.serializer(), it)) }
?: Response(NOT_FOUND)
}
fun update(request: Request, jwtPayload: JwtPayload): Response {
val uuid = UUID.fromString(request.path("uuid")!!)
val content = json.decodeFromString(NoteContent.serializer(), request.bodyString()).content
return noteService.update(jwtPayload.userId, uuid, content).fold({
Response(BAD_REQUEST)
}, {
if (it == null) Response(NOT_FOUND)
else Response(OK)
})
}
fun search(request: Request, jwtPayload: JwtPayload): Response {
val query = json.decodeFromString(SearchContent.serializer(), request.bodyString()).query
val terms = parseSearchTerms(query)
val notes = noteService.search(jwtPayload.userId, terms)
val json = json.encodeToString(ListSerializer(PersistedNoteMetadata.serializer()), notes)
return Response(OK).json(json)
}
}
@Serializable
data class NoteContent(val content: String)
@Serializable
data class UuidContent(@Contextual val uuid: UUID)
@Serializable
data class SearchContent(@Contextual val query: String)
@@ -1,26 +0,0 @@
package be.simplenotes.app.api
import be.simplenotes.app.extensions.json
import be.simplenotes.domain.usecases.UserService
import be.simplenotes.domain.usecases.users.login.LoginForm
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status
class ApiUserController(private val userService: UserService, private val json: Json) {
fun login(request: Request): Response {
val form = json.decodeFromString(LoginForm.serializer(), request.bodyString())
val result = userService.login(form)
return result.fold({
Response(Status.BAD_REQUEST)
}, {
Response(Status.OK).json(json.encodeToString(Token.serializer(), Token(it)))
})
}
}
@Serializable
data class Token(val token: String)
@@ -1,17 +0,0 @@
package be.simplenotes.app.extensions
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.FOUND
import org.http4k.core.Status.Companion.MOVED_PERMANENTLY
fun Response.html(html: String) = body(html)
.header("Content-Type", "text/html; charset=utf-8")
.header("Cache-Control", "no-cache")
fun Response.json(json: String) = body(json).header("Content-Type", "application/json")
fun Response.Companion.redirect(url: String, permanent: Boolean = false) =
Response(if (permanent) MOVED_PERMANENTLY else FOUND).header("Location", url)
fun Request.isSecure() = header("X-Forwarded-Proto")?.contains("https") ?: false
@@ -1,11 +0,0 @@
package be.simplenotes.app.modules
import be.simplenotes.app.Config
import org.koin.dsl.module
val configModule = module {
single { Config() }
single { get<Config>().dataSourceConfig }
single { get<Config>().jwtConfig }
single { get<Config>().serverConfig }
}
@@ -1,29 +0,0 @@
package be.simplenotes.app.modules
import be.simplenotes.app.controllers.*
import be.simplenotes.app.views.BaseView
import be.simplenotes.app.views.NoteView
import be.simplenotes.app.views.SettingView
import be.simplenotes.app.views.UserView
import org.koin.dsl.module
val userModule = module {
single { UserController(get(), get(), get()) }
single { UserView(get()) }
}
val baseModule = module {
single { HealthCheckController(get()) }
single { BaseController(get()) }
single { BaseView(get()) }
}
val noteModule = module {
single { NoteController(get(), get()) }
single { NoteView(get()) }
}
val settingsModule = module {
single { SettingsController(get(), get()) }
single { SettingView(get()) }
}
-10
View File
@@ -1,10 +0,0 @@
package be.simplenotes.app.utils
import org.ocpsoft.prettytime.PrettyTime
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
private val prettyTime = PrettyTime()
fun LocalDateTime.toTimeAgo(): String = prettyTime.format(Date.from(atZone(ZoneId.systemDefault()).toInstant()))
-1
View File
@@ -1 +0,0 @@
package be.simplenotes.app
+1
View File
@@ -0,0 +1 @@
org.gradle.caching=true
+15
View File
@@ -0,0 +1,15 @@
plugins {
`kotlin-dsl`
kotlin("jvm") version "1.4.10"
id("com.github.johnrengelman.shadow") version "6.1.0"
}
repositories {
gradlePluginPortal()
maven { setUrl("https://kotlin.bintray.com/kotlinx") }
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10")
implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0")
}
@@ -0,0 +1,52 @@
package be.simplenotes
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.lang.ProcessBuilder.Redirect.PIPE
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
open class CssTask : DefaultTask() {
private val root = project.file(".").parent
@get:InputDirectory
val templatesDir = File(root, "simplenotes-views/src/main/kotlin/be/simplenotes/views")
@get:InputDirectory
val postCssDir = File(project.rootDir, "css/src")
@get:OutputDirectory
val outputRootDir = File(project.buildDir, "generated-resources/css")
private val yarnRoot = File(project.rootDir, "css")
private val cssIndex = File(postCssDir, "styles.pcss")
private val cssOutput = File(outputRootDir, "static/styles.css")
private val manifestOutput = File(outputRootDir, "css-manifest.json")
@TaskAction
fun generateCss() {
// TODO: auto yarn install ?
outputRootDir.listFiles()?.let { it.forEach { it.delete() } }
ProcessBuilder("yarn", "run", "postcss", "build", "$cssIndex", "--output", "$cssOutput")
.apply { environment()["MANIFEST"] = "$manifestOutput" }
.redirectOutput(PIPE)
.redirectError(PIPE)
.directory(yarnRoot)
.start()
.apply {
thread { inputStream.use { it.copyTo(System.out) } }
thread { errorStream.use { it.copyTo(System.out) } }
waitFor(30, TimeUnit.SECONDS)
if (exitValue() != 0) throw GradleException(":/")
}
}
}
@@ -0,0 +1,42 @@
@file:Suppress("SpellCheckingInspection")
package be.simplenotes
object Libs {
const val arrowCore = "io.arrow-kt:arrow-core:0.11.0"
const val commonsCompress = "org.apache.commons:commons-compress:1.20"
const val flexmark = "com.vladsch.flexmark:flexmark:0.62.2"
const val flexmarkGfmTasklist = "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.62.2"
const val flywayCore = "org.flywaydb:flyway-core:6.5.4"
const val h2 = "com.h2database:h2:1.4.200"
const val hikariCP = "com.zaxxer:HikariCP:3.4.3"
const val http4kCore = "org.http4k:http4k-core:3.268.0"
const val javaJwt = "com.auth0:java-jwt:3.10.3"
const val javaxServlet = "javax.servlet:javax.servlet-api:4.0.1"
const val jbcrypt = "org.mindrot:jbcrypt:0.4"
const val jettyServer = "org.eclipse.jetty:jetty-server:9.4.32.v20200930"
const val jettyServlet = "org.eclipse.jetty:jetty-servlet:9.4.32.v20200930"
const val koinCore = "org.koin:koin-core:2.1.6"
const val konform = "io.konform:konform-jvm:0.2.0"
const val kotlinxHtml = "org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.1"
const val kotlinxSerializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.0.0"
const val ktormCore = "me.liuwj.ktorm:ktorm-core:3.0.0"
const val ktormMysql = "me.liuwj.ktorm:ktorm-support-mysql:3.0.0"
const val logbackClassic = "ch.qos.logback:logback-classic:1.2.3"
const val luceneAnalyzersCommon = "org.apache.lucene:lucene-analyzers-common:8.6.1"
const val luceneCore = "org.apache.lucene:lucene-core:8.6.1"
const val luceneQueryParser = "org.apache.lucene:lucene-queryparser:8.6.1"
const val mapstruct = "org.mapstruct:mapstruct:1.4.1.Final"
const val mapstructProcessor = "org.mapstruct:mapstruct-processor:1.4.1.Final"
const val mariadbClient = "org.mariadb.jdbc:mariadb-java-client:2.6.2"
const val owaspHtmlSanitizer = "com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20200713.1"
const val prettytime ="org.ocpsoft.prettytime:prettytime:4.0.5.Final"
const val slf4jApi = "org.slf4j:slf4j-api:1.7.25"
const val snakeyaml = "org.yaml:snakeyaml:1.26"
const val assertJ = "org.assertj:assertj-core:3.16.1"
const val hamkrest = "com.natpryce:hamkrest:1.7.0.3"
const val http4kTestingHamkrest = "org.http4k:http4k-testing-hamkrest:3.268.0"
const val junit = "org.junit.jupiter:junit-jupiter:5.6.2"
const val mockk = "io.mockk:mockk:1.10.0"
}
@@ -0,0 +1,15 @@
package be.simplenotes
import org.gradle.kotlin.dsl.register
plugins {
java apply false
}
tasks.register<CssTask>("css")
sourceSets {
val main by getting
val root = file("$buildDir/generated-resources/css")
main.resources.srcDir(root)
}
@@ -0,0 +1,39 @@
package be.simplenotes
import java.util.concurrent.TimeUnit.MINUTES
import kotlin.concurrent.thread
fun runCommand(vararg args: String, onError: () -> Unit) {
logging.captureStandardOutput(LogLevel.INFO)
ProcessBuilder(*args)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.directory(rootProject.projectDir)
.start()
.apply {
thread { inputStream.use { it.copyTo(System.out) } }
thread { errorStream.use { it.copyTo(System.out) } }
waitFor(2, MINUTES)
if (exitValue() != 0) onError()
}
}
tasks.create("dockerBuild") {
dependsOn("package")
doLast {
runCommand("docker", "build", "-t", "hubv/simplenotes:latest", ".") {
throw GradleException("Docker build failed")
}
}
}
tasks.create("dockerPush") {
dependsOn("dockerBuild")
doLast {
runCommand("docker", "push", "hubv/simplenotes:latest") {
throw GradleException("Docker Push failed")
}
}
}
@@ -0,0 +1,47 @@
package be.simplenotes
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
id("com.github.johnrengelman.shadow")
}
tasks.withType<ShadowJar> {
archiveBaseName.set("app")
manifest.attributes["Main-Class"] = "be.simplenotes.app.SimpleNotesKt"
mergeServiceFiles()
// minimize()
// include("org.mariadb.jdbc:mariadb-java-client")
// include("com.h2database:h2")
// include("org.jetbrains.kotlin:kotlin-reflect")
// include("org.eclipse.jetty:*")
// include("org.apache.lucene:*")
// include("org.ocpsoft.prettytime:prettytime")
File(rootProject.projectDir, "buildSrc/src/main/resources/exclusions")
.listFiles()!!
.flatMap {
it.readLines()
.asSequence()
.map { it.trim() }
.filterNot { it.isBlank() }
.filterNot { it.startsWith("#") }
.asIterable()
}.forEach { exclude(it) }
}
tasks.create("package") {
rootProject.subprojects.forEach { dependsOn(":${it.name}:test") }
dependsOn("shadowJar")
dependsOn("css")
doLast {
println("SimpleNotes Packaged !")
}
}
@@ -0,0 +1,55 @@
package be.simplenotes
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
java
kotlin("jvm")
`java-library`
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven { url = uri("https://dl.bintray.com/arrow-kt/arrow-kt/") }
maven { url = uri("https://kotlin.bintray.com/kotlinx") }
}
group = "be.simplenotes"
version = "1.0-SNAPSHOT"
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
tasks.withType<Test> {
useJUnitPlatform()
}
java {
sourceCompatibility = JavaVersion.VERSION_14
targetCompatibility = JavaVersion.VERSION_14
}
sourceSets {
val test by getting
test.resources.srcDir("${rootProject.projectDir}/simplenotes-test-resources/src/test/resources")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "14"
javaParameters = true
freeCompilerArgs = listOf(
"-Xinline-classes",
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
@@ -0,0 +1,14 @@
META-INF/maven/**
META-INF/proguard/**
META-INF/*.kotlin_module
META-INF/DEPENDENCIES*
META-INF/NOTICE*
META-INF/LICENSE*
LICENSE*
META-INF/README*
META-INF/native-image/**
# Jetty
about.html
jetty-dir.css
server-ssl-cert.pem
@@ -0,0 +1,261 @@
com/google/common/util/**
com/google/common/eventbus/**
com/google/common/reflect/**
com/google/common/escape/**
com/google/common/graph/**
com/google/common/html/**
com/google/common/hash/**
com/google/common/xml/**
com/google/common/io/**
com/google/common/cache/**
com/google/common/net/**
# Collections
com/google/common/collect/AbstractBiMap$1.class
com/google/common/collect/AbstractBiMap$BiMapEntry.class
com/google/common/collect/AbstractBiMap$EntrySet.class
com/google/common/collect/AbstractBiMap$Inverse.class
com/google/common/collect/AbstractBiMap$KeySet.class
com/google/common/collect/AbstractBiMap$ValueSet.class
com/google/common/collect/AbstractBiMap.class
com/google/common/collect/AbstractSortedKeySortedSetMultimap.class
com/google/common/collect/AbstractSortedMultiset$1DescendingMultisetImpl.class
com/google/common/collect/AbstractSortedMultiset.class
com/google/common/collect/AbstractTable$1.class
com/google/common/collect/AbstractTable$CellSet.class
com/google/common/collect/AbstractTable$Values.class
com/google/common/collect/AbstractTable.class
com/google/common/collect/ArrayListMultimap.class
com/google/common/collect/ArrayListMultimapGwtSerializationDependencies.class
com/google/common/collect/ArrayTable$1.class
com/google/common/collect/ArrayTable$2.class
com/google/common/collect/ArrayTable$3.class
com/google/common/collect/ArrayTable$ArrayMap$1.class
com/google/common/collect/ArrayTable$ArrayMap$2.class
com/google/common/collect/ArrayTable$ArrayMap.class
com/google/common/collect/ArrayTable$Column.class
com/google/common/collect/ArrayTable$ColumnMap.class
com/google/common/collect/ArrayTable$Row.class
com/google/common/collect/ArrayTable$RowMap.class
com/google/common/collect/ArrayTable.class
com/google/common/collect/ClassToInstanceMap.class
com/google/common/collect/CompactHashMap$1.class
com/google/common/collect/CompactHashMap$2.class
com/google/common/collect/CompactHashMap$3.class
com/google/common/collect/CompactHashMap$EntrySetView.class
com/google/common/collect/CompactHashMap$Itr.class
com/google/common/collect/CompactHashMap$KeySetView.class
com/google/common/collect/CompactHashMap$MapEntry.class
com/google/common/collect/CompactHashMap$ValuesView.class
com/google/common/collect/CompactHashMap.class
com/google/common/collect/CompactHashSet$1.class
com/google/common/collect/CompactHashSet.class
com/google/common/collect/CompactLinkedHashMap$1EntrySetImpl.class
com/google/common/collect/CompactLinkedHashMap$1KeySetImpl.class
com/google/common/collect/CompactLinkedHashMap$1ValuesImpl.class
com/google/common/collect/CompactLinkedHashMap.class
com/google/common/collect/CompactLinkedHashSet.class
com/google/common/collect/Comparators.class
com/google/common/collect/ComputationException.class
com/google/common/collect/ConcurrentHashMultiset$1.class
com/google/common/collect/ConcurrentHashMultiset$2.class
com/google/common/collect/ConcurrentHashMultiset$3.class
com/google/common/collect/ConcurrentHashMultiset$EntrySet.class
com/google/common/collect/ConcurrentHashMultiset$FieldSettersHolder.class
com/google/common/collect/ConcurrentHashMultiset.class
com/google/common/collect/DenseImmutableTable$1.class
com/google/common/collect/DenseImmutableTable$Column.class
com/google/common/collect/DenseImmutableTable$ColumnMap.class
com/google/common/collect/DenseImmutableTable$ImmutableArrayMap$1.class
com/google/common/collect/DenseImmutableTable$ImmutableArrayMap.class
com/google/common/collect/DenseImmutableTable$Row.class
com/google/common/collect/DenseImmutableTable$RowMap.class
com/google/common/collect/DenseImmutableTable.class
com/google/common/collect/DescendingImmutableSortedMultiset.class
com/google/common/collect/DescendingMultiset$1EntrySetImpl.class
com/google/common/collect/DescendingMultiset.class
com/google/common/collect/EnumBiMap.class
com/google/common/collect/EnumHashBiMap.class
com/google/common/collect/EnumMultiset$1.class
com/google/common/collect/EnumMultiset$2$1.class
com/google/common/collect/EnumMultiset$2.class
com/google/common/collect/EnumMultiset$Itr.class
com/google/common/collect/EnumMultiset.class
com/google/common/collect/EvictingQueue.class
com/google/common/collect/ForwardingBlockingDeque.class
com/google/common/collect/ForwardingDeque.class
com/google/common/collect/ForwardingImmutableCollection.class
com/google/common/collect/ForwardingImmutableList.class
com/google/common/collect/ForwardingImmutableMap.class
com/google/common/collect/ForwardingImmutableSet.class
com/google/common/collect/ForwardingIterator.class
com/google/common/collect/ForwardingListIterator.class
com/google/common/collect/ForwardingListMultimap.class
com/google/common/collect/ForwardingNavigableMap$StandardDescendingMap$1.class
com/google/common/collect/ForwardingNavigableMap$StandardDescendingMap.class
com/google/common/collect/ForwardingNavigableMap$StandardNavigableKeySet.class
com/google/common/collect/ForwardingNavigableMap.class
com/google/common/collect/ForwardingQueue.class
com/google/common/collect/ForwardingSetMultimap.class
com/google/common/collect/ForwardingSortedMultiset$StandardDescendingMultiset.class
com/google/common/collect/ForwardingSortedMultiset$StandardElementSet.class
com/google/common/collect/ForwardingSortedMultiset.class
com/google/common/collect/ForwardingSortedSetMultimap.class
com/google/common/collect/ForwardingTable.class
com/google/common/collect/GeneralRange.class
com/google/common/collect/GwtTransient.class
com/google/common/collect/HashBasedTable$Factory.class
com/google/common/collect/HashBasedTable.class
com/google/common/collect/HashBiMap$1$MapEntry.class
com/google/common/collect/HashBiMap$1.class
com/google/common/collect/HashBiMap$BiEntry.class
com/google/common/collect/HashBiMap$Inverse$1$InverseEntry.class
com/google/common/collect/HashBiMap$Inverse$1.class
com/google/common/collect/HashBiMap$Inverse$InverseKeySet$1.class
com/google/common/collect/HashBiMap$Inverse$InverseKeySet.class
com/google/common/collect/HashBiMap$Inverse.class
com/google/common/collect/HashBiMap$InverseSerializedForm.class
com/google/common/collect/HashBiMap$Itr.class
com/google/common/collect/HashBiMap$KeySet$1.class
com/google/common/collect/HashBiMap$KeySet.class
com/google/common/collect/HashBiMap.class
com/google/common/collect/HashMultimap.class
com/google/common/collect/HashMultimapGwtSerializationDependencies.class
com/google/common/collect/ImmutableClassToInstanceMap$1.class
com/google/common/collect/ImmutableClassToInstanceMap$Builder.class
com/google/common/collect/ImmutableClassToInstanceMap.class
com/google/common/collect/ImmutableSortedMultiset$Builder.class
com/google/common/collect/ImmutableSortedMultiset$SerializedForm.class
com/google/common/collect/ImmutableSortedMultiset.class
com/google/common/collect/ImmutableSortedMultisetFauxverideShim.class
com/google/common/collect/ImmutableTable$1.class
com/google/common/collect/ImmutableTable$Builder.class
com/google/common/collect/ImmutableTable$CollectorState.class
com/google/common/collect/ImmutableTable$MutableCell.class
com/google/common/collect/ImmutableTable$SerializedForm.class
com/google/common/collect/ImmutableTable.class
com/google/common/collect/Interner.class
com/google/common/collect/Interners$1.class
com/google/common/collect/Interners$InternerBuilder.class
com/google/common/collect/Interners$InternerFunction.class
com/google/common/collect/Interners$InternerImpl.class
com/google/common/collect/Interners.class
com/google/common/collect/LinkedHashMultimap$1.class
com/google/common/collect/LinkedHashMultimap$ValueEntry.class
com/google/common/collect/LinkedHashMultimap$ValueSet$1.class
com/google/common/collect/LinkedHashMultimap$ValueSet.class
com/google/common/collect/LinkedHashMultimap$ValueSetLink.class
com/google/common/collect/LinkedHashMultimap.class
com/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.class
com/google/common/collect/LinkedListMultimap$1.class
com/google/common/collect/LinkedListMultimap$1EntriesImpl.class
com/google/common/collect/LinkedListMultimap$1KeySetImpl.class
com/google/common/collect/LinkedListMultimap$1ValuesImpl$1.class
com/google/common/collect/LinkedListMultimap$1ValuesImpl.class
com/google/common/collect/LinkedListMultimap$DistinctKeyIterator.class
com/google/common/collect/LinkedListMultimap$KeyList.class
com/google/common/collect/LinkedListMultimap$Node.class
com/google/common/collect/LinkedListMultimap$NodeIterator.class
com/google/common/collect/LinkedListMultimap$ValueForKeyIterator.class
com/google/common/collect/LinkedListMultimap.class
com/google/common/collect/MinMaxPriorityQueue$1.class
com/google/common/collect/MinMaxPriorityQueue$Builder.class
com/google/common/collect/MinMaxPriorityQueue$Heap.class
com/google/common/collect/MinMaxPriorityQueue$MoveDesc.class
com/google/common/collect/MinMaxPriorityQueue$QueueIterator.class
com/google/common/collect/MinMaxPriorityQueue.class
com/google/common/collect/MoreCollectors$ToOptionalState.class
com/google/common/collect/MoreCollectors.class
com/google/common/collect/MutableClassToInstanceMap$1.class
com/google/common/collect/MutableClassToInstanceMap$2$1.class
com/google/common/collect/MutableClassToInstanceMap$2.class
com/google/common/collect/MutableClassToInstanceMap$SerializedForm.class
com/google/common/collect/MutableClassToInstanceMap.class
com/google/common/collect/Queues.class
com/google/common/collect/RegularImmutableSortedMultiset.class
com/google/common/collect/RegularImmutableTable$1.class
com/google/common/collect/RegularImmutableTable$CellSet.class
com/google/common/collect/RegularImmutableTable$Values.class
com/google/common/collect/RegularImmutableTable.class
com/google/common/collect/RowSortedTable.class
com/google/common/collect/SingletonImmutableTable.class
com/google/common/collect/SortedMultisets$ElementSet.class
com/google/common/collect/SortedMultisets$NavigableElementSet.class
com/google/common/collect/SortedMultisets.class
com/google/common/collect/SparseImmutableTable.class
com/google/common/collect/StandardRowSortedTable$1.class
com/google/common/collect/StandardRowSortedTable$RowSortedMap.class
com/google/common/collect/StandardRowSortedTable.class
com/google/common/collect/StandardTable$1.class
com/google/common/collect/StandardTable$CellIterator.class
com/google/common/collect/StandardTable$Column$EntrySet.class
com/google/common/collect/StandardTable$Column$EntrySetIterator$1EntryImpl.class
com/google/common/collect/StandardTable$Column$EntrySetIterator.class
com/google/common/collect/StandardTable$Column$KeySet.class
com/google/common/collect/StandardTable$Column$Values.class
com/google/common/collect/StandardTable$Column.class
com/google/common/collect/StandardTable$ColumnKeyIterator.class
com/google/common/collect/StandardTable$ColumnKeySet.class
com/google/common/collect/StandardTable$ColumnMap$ColumnMapEntrySet$1.class
com/google/common/collect/StandardTable$ColumnMap$ColumnMapEntrySet.class
com/google/common/collect/StandardTable$ColumnMap$ColumnMapValues.class
com/google/common/collect/StandardTable$ColumnMap.class
com/google/common/collect/StandardTable$Row$1.class
com/google/common/collect/StandardTable$Row$2.class
com/google/common/collect/StandardTable$Row.class
com/google/common/collect/StandardTable$RowMap$EntrySet$1.class
com/google/common/collect/StandardTable$RowMap$EntrySet.class
com/google/common/collect/StandardTable$RowMap.class
com/google/common/collect/StandardTable$TableSet.class
com/google/common/collect/StandardTable.class
com/google/common/collect/Tables$1.class
com/google/common/collect/Tables$AbstractCell.class
com/google/common/collect/Tables$ImmutableCell.class
com/google/common/collect/Tables$TransformedTable$1.class
com/google/common/collect/Tables$TransformedTable$2.class
com/google/common/collect/Tables$TransformedTable$3.class
com/google/common/collect/Tables$TransformedTable.class
com/google/common/collect/Tables$TransposeTable$1.class
com/google/common/collect/Tables$TransposeTable.class
com/google/common/collect/Tables$UnmodifiableRowSortedMap.class
com/google/common/collect/Tables$UnmodifiableTable.class
com/google/common/collect/Tables.class
com/google/common/collect/TreeBasedTable$1.class
com/google/common/collect/TreeBasedTable$2.class
com/google/common/collect/TreeBasedTable$Factory.class
com/google/common/collect/TreeBasedTable$TreeRow.class
com/google/common/collect/TreeBasedTable.class
com/google/common/collect/TreeMultimap.class
com/google/common/collect/TreeMultiset$1.class
com/google/common/collect/TreeMultiset$2.class
com/google/common/collect/TreeMultiset$3.class
com/google/common/collect/TreeMultiset$4.class
com/google/common/collect/TreeMultiset$Aggregate$1.class
com/google/common/collect/TreeMultiset$Aggregate$2.class
com/google/common/collect/TreeMultiset$Aggregate.class
com/google/common/collect/TreeMultiset$AvlNode.class
com/google/common/collect/TreeMultiset$Reference.class
com/google/common/collect/TreeMultiset.class
com/google/common/collect/TreeRangeMap$1.class
com/google/common/collect/TreeRangeMap$AsMapOfRanges.class
com/google/common/collect/TreeRangeMap$RangeMapEntry.class
com/google/common/collect/TreeRangeMap$SubRangeMap$1$1.class
com/google/common/collect/TreeRangeMap$SubRangeMap$1.class
com/google/common/collect/TreeRangeMap$SubRangeMap$SubRangeMapAsMap$1.class
com/google/common/collect/TreeRangeMap$SubRangeMap$SubRangeMapAsMap$2.class
com/google/common/collect/TreeRangeMap$SubRangeMap$SubRangeMapAsMap$3.class
com/google/common/collect/TreeRangeMap$SubRangeMap$SubRangeMapAsMap$4.class
com/google/common/collect/TreeRangeMap$SubRangeMap$SubRangeMapAsMap.class
com/google/common/collect/TreeRangeMap$SubRangeMap.class
com/google/common/collect/TreeRangeMap.class
com/google/common/collect/TreeTraverser$1.class
com/google/common/collect/TreeTraverser$2$1.class
com/google/common/collect/TreeTraverser$2.class
com/google/common/collect/TreeTraverser$3$1.class
com/google/common/collect/TreeTraverser$3.class
com/google/common/collect/TreeTraverser$4.class
com/google/common/collect/TreeTraverser$BreadthFirstIterator.class
com/google/common/collect/TreeTraverser$PostOrderIterator.class
com/google/common/collect/TreeTraverser$PostOrderNode.class
com/google/common/collect/TreeTraverser$PreOrderIterator.class
com/google/common/collect/TreeTraverser.class
@@ -0,0 +1,2 @@
ch/qos/logback/core/db/**
ch/qos/logback/classic/db/**
@@ -0,0 +1,6 @@
arrow/core/extensions/**
org/checkerframework/**
org/intellij/**
com/google/errorprone/**
com/google/thirdparty/**
com/google/j2objc/**
+1 -2
View File
@@ -2,8 +2,7 @@
"name": "css", "name": "css",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"css": "NODE_ENV=dev MANIFEST=../app/src/main/resources/css-manifest.json postcss build src/styles.pcss --output ../app/src/main/resources/static/styles.css", "//": "`gradle css`"
"css-purge": "NODE_ENV=production MANIFEST=../app/src/main/resources/css-manifest.json postcss build src/styles.pcss --output ../app/src/main/resources/static/styles.css"
}, },
"dependencies": { "dependencies": {
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
+1 -1
View File
@@ -1,7 +1,7 @@
module.exports = { module.exports = {
purge: { purge: {
content: [ content: [
'../app/src/main/kotlin/views/**/*.kt' '../simplenotes-app/src/main/kotlin/be/simplenotes/app/views/**/*.kt'
] ]
}, },
theme: { theme: {
-8
View File
@@ -1,8 +0,0 @@
#!/bin/sh
rm app/src/main/resources/css-manifest.json
rm app/src/main/resources/static/styles*
yarn --cwd css run css-purge \
&& docker build -t hubv/simplenotes:latest . \
&& docker push hubv/simplenotes:latest
-97
View File
@@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>be.simplenotes</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>domain</artifactId>
<dependencies>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.arrow-kt</groupId>
<artifactId>arrow-core</artifactId>
</dependency>
<dependency>
<groupId>org.koin</groupId>
<artifactId>koin-core</artifactId>
</dependency>
<dependency>
<groupId>com.natpryce</groupId>
<artifactId>hamkrest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.konform</groupId>
<artifactId>konform-jvm</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark</artifactId>
<version>0.62.2</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-ext-gfm-tasklist</artifactId>
<version>0.62.2</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.26</version>
</dependency>
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
<version>20200713.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json-jvm</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.20</version>
</dependency>
</dependencies>
</project>
-4
View File
@@ -1,4 +0,0 @@
package be.simplenotes.domain.model
data class User(val username: String, val password: String)
data class PersistedUser(val username: String, val password: String, val id: Int)
-5
View File
@@ -1,5 +0,0 @@
package be.simplenotes.domain
/**
* Empty file @see [root-package-declaration](https://discuss.kotlinlang.org/t/root-package-declaration-to-reduce-folder-clutter/2247/4)
*/
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
Vendored
+89
View File
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-77
View File
@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>be.simplenotes</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>persistance</artifactId>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>domain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>6.5.4</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>me.liuwj.ktorm</groupId>
<artifactId>ktorm-core</artifactId>
</dependency>
<dependency>
<groupId>me.liuwj.ktorm</groupId>
<artifactId>ktorm-support-mysql</artifactId>
</dependency>
</dependencies>
</project>
@@ -1,16 +0,0 @@
package be.simplenotes.persistance.converters
import be.simplenotes.domain.model.PersistedUser
import be.simplenotes.domain.model.User
import be.simplenotes.persistance.users.UserEntity
import me.liuwj.ktorm.entity.Entity
import org.mapstruct.Mapper
@Mapper(uses = [UserEntityFactory::class])
internal interface UserConverter {
fun convertToUser(userEntity: UserEntity): User
fun convertToPersistedUser(userEntity: UserEntity): PersistedUser
fun convertToEntity(user: User): UserEntity
}
internal class UserEntityFactory : Entity.Factory<UserEntity>()
-1
View File
@@ -1 +0,0 @@
package be.simplenotes.persistance
-247
View File
@@ -1,247 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>be.simplenotes</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>persistance</module>
<module>app</module>
<module>domain</module>
<module>shared</module>
<module>search</module>
</modules>
<packaging>pom</packaging>
<properties>
<java.version>14</java.version>
<kotlin.version>1.4.10</kotlin.version>
<junit.version>5.6.2</junit.version>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<kotlin.compiler.jvmTarget>${java.version}</kotlin.compiler.jvmTarget>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit-platform</artifactId>
<version>3.0.0-M5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M3</version>
<executions>
<execution>
<id>enforce</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<banDuplicatePomDependencyVersions/>
<requireMavenVersion>
<version>3.6</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-Xno-param-assertions</arg>
<arg>-Xno-call-assertions</arg>
<arg>-Xno-receiver-assertions</arg>
</args>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-bom</artifactId>
<version>${kotlin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json-jvm</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.koin</groupId>
<artifactId>koin-core</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>io.arrow-kt</groupId>
<artifactId>arrow-core</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>me.liuwj.ktorm</groupId>
<artifactId>ktorm-core</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>me.liuwj.ktorm</groupId>
<artifactId>ktorm-support-mysql</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- region tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>com.natpryce</groupId>
<artifactId>hamkrest</artifactId>
<version>1.7.0.3</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.16.1</version>
</dependency>
<!-- endregion -->
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>jcenter</id>
<url>https://jcenter.bintray.com</url>
</repository>
<repository>
<id>arrow</id>
<url>https://dl.bintray.com/arrow-kt/arrow-kt/</url>
</repository>
<repository>
<id>kotlinx</id>
<url>https://kotlin.bintray.com/kotlinx</url>
</repository>
</repositories>
</project>
-66
View File
@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>be.simplenotes</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>search</artifactId>
<properties>
<lucene.version>8.6.1</lucene.version>
</properties>
<dependencies>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>domain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>be.simplenotes</groupId>
<artifactId>shared</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>
+8
View File
@@ -0,0 +1,8 @@
rootProject.name = "simplenotes"
include(":simplenotes-config")
include(":simplenotes-views")
include(":simplenotes-app")
include(":simplenotes-domain")
include(":simplenotes-search")
include(":simplenotes-types")
include(":simplenotes-persistance")
-41
View File
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>be.simplenotes</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shared</artifactId>
<dependencies>
<dependency>
<groupId>io.arrow-kt</groupId>
<artifactId>arrow-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.natpryce</groupId>
<artifactId>hamkrest</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
-1
View File
@@ -1 +0,0 @@
package be.simplenotes.shared
+32
View File
@@ -0,0 +1,32 @@
import be.simplenotes.Libs
plugins {
id("be.simplenotes.base")
id("be.simplenotes.app-shadow")
id("be.simplenotes.app-css")
id("be.simplenotes.app-docker")
}
dependencies {
implementation(project(":simplenotes-persistance"))
implementation(project(":simplenotes-search"))
implementation(project(":simplenotes-domain"))
implementation(project(":simplenotes-types"))
implementation(project(":simplenotes-config"))
implementation(project(":simplenotes-views"))
implementation(Libs.koinCore)
implementation(Libs.arrowCore)
implementation(Libs.konform)
implementation(Libs.http4kCore)
implementation(Libs.jettyServer)
implementation(Libs.jettyServlet)
implementation(Libs.javaxServlet)
implementation(Libs.kotlinxSerializationJson)
implementation(Libs.logbackClassic)
implementation(Libs.ktormCore)
testImplementation(Libs.junit)
testImplementation(Libs.assertJ)
testImplementation(Libs.http4kTestingHamkrest)
}
@@ -2,7 +2,7 @@ package be.simplenotes.app
import org.http4k.server.Http4kServer import org.http4k.server.Http4kServer
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import be.simplenotes.shared.config.ServerConfig as SimpleNotesServerConfig import be.simplenotes.config.ServerConfig as SimpleNotesServerConfig
class Server( class Server(
private val config: SimpleNotesServerConfig, private val config: SimpleNotesServerConfig,
@@ -2,10 +2,12 @@ package be.simplenotes.app
import be.simplenotes.app.extensions.addShutdownHook import be.simplenotes.app.extensions.addShutdownHook
import be.simplenotes.app.modules.* import be.simplenotes.app.modules.*
import be.simplenotes.config.configModule
import be.simplenotes.domain.domainModule import be.simplenotes.domain.domainModule
import be.simplenotes.persistance.migrationModule import be.simplenotes.persistance.migrationModule
import be.simplenotes.persistance.persistanceModule import be.simplenotes.persistance.persistanceModule
import be.simplenotes.search.searchModule import be.simplenotes.search.searchModule
import be.simplenotes.views.viewModule
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.core.context.unloadKoinModules import org.koin.core.context.unloadKoinModules
@@ -16,10 +18,8 @@ fun main() {
persistanceModule, persistanceModule,
migrationModule, migrationModule,
configModule, configModule,
baseModule, viewModule,
userModule, controllerModule,
noteModule,
settingsModule,
domainModule, domainModule,
searchModule, searchModule,
apiModule, apiModule,
@@ -0,0 +1,74 @@
package be.simplenotes.app.api
import be.simplenotes.app.extensions.auto
import be.simplenotes.app.utils.parseSearchTerms
import be.simplenotes.types.PersistedNote
import be.simplenotes.types.PersistedNoteMetadata
import be.simplenotes.domain.usecases.NoteService
import be.simplenotes.types.LoggedInUser
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.BAD_REQUEST
import org.http4k.core.Status.Companion.NOT_FOUND
import org.http4k.core.Status.Companion.OK
import org.http4k.lens.Path
import org.http4k.lens.uuid
import java.util.*
class ApiNoteController(private val noteService: NoteService, private val json: Json) {
fun createNote(request: Request, loggedInUser: LoggedInUser): Response {
val content = noteContentLens(request)
return noteService.create(loggedInUser.userId, content).fold(
{ Response(BAD_REQUEST) },
{ uuidContentLens(UuidContent(it.uuid), Response(OK)) }
)
}
fun notes(request: Request, loggedInUser: LoggedInUser): Response {
val notes = noteService.paginatedNotes(loggedInUser.userId, page = 1).notes
return persistedNotesMetadataLens(notes, Response(OK))
}
fun note(request: Request, loggedInUser: LoggedInUser): Response =
noteService.find(loggedInUser.userId, uuidLens(request))
?.let { persistedNoteLens(it, Response(OK)) }
?: Response(NOT_FOUND)
fun update(request: Request, loggedInUser: LoggedInUser): Response {
val content = noteContentLens(request)
return noteService.update(loggedInUser.userId, uuidLens(request), content).fold({
Response(BAD_REQUEST)
}, {
if (it == null) Response(NOT_FOUND)
else Response(OK)
})
}
fun search(request: Request, loggedInUser: LoggedInUser): Response {
val query = searchContentLens(request)
val terms = parseSearchTerms(query)
val notes = noteService.search(loggedInUser.userId, terms)
return persistedNotesMetadataLens(notes, Response(OK))
}
private val uuidContentLens = json.auto<UuidContent>().toLens()
private val noteContentLens = json.auto<NoteContent>().map { it.content }.toLens()
private val searchContentLens = json.auto<SearchContent>().map { it.query }.toLens()
private val persistedNotesMetadataLens = json.auto<List<PersistedNoteMetadata>>().toLens()
private val persistedNoteLens = json.auto<PersistedNote>().toLens()
private val uuidLens = Path.uuid().of("uuid")
}
@Serializable
data class NoteContent(val content: String)
@Serializable
data class UuidContent(@Contextual val uuid: UUID)
@Serializable
data class SearchContent(@Contextual val query: String)
@@ -0,0 +1,26 @@
package be.simplenotes.app.api
import be.simplenotes.app.extensions.auto
import be.simplenotes.domain.usecases.UserService
import be.simplenotes.domain.usecases.users.login.LoginForm
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.BAD_REQUEST
import org.http4k.core.Status.Companion.OK
class ApiUserController(private val userService: UserService, private val json: Json) {
private val tokenLens = json.auto<Token>().toLens()
private val loginFormLens = json.auto<LoginForm>().toLens()
fun login(request: Request) = userService
.login(loginFormLens(request))
.fold(
{ Response(BAD_REQUEST) },
{ tokenLens(Token(it), Response(OK)) }
)
}
@Serializable
data class Token(val token: String)
@@ -1,13 +1,13 @@
package be.simplenotes.app.controllers package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html import be.simplenotes.app.extensions.html
import be.simplenotes.app.views.BaseView import be.simplenotes.types.LoggedInUser
import be.simplenotes.domain.security.JwtPayload import be.simplenotes.views.BaseView
import org.http4k.core.Request import org.http4k.core.Request
import org.http4k.core.Response import org.http4k.core.Response
import org.http4k.core.Status.Companion.OK import org.http4k.core.Status.Companion.OK
class BaseController(private val view: BaseView) { class BaseController(private val view: BaseView) {
fun index(@Suppress("UNUSED_PARAMETER") request: Request, jwtPayload: JwtPayload?) = fun index(@Suppress("UNUSED_PARAMETER") request: Request, loggedInUser: LoggedInUser?) =
Response(OK).html(view.renderHome(jwtPayload)) Response(OK).html(view.renderHome(loggedInUser))
} }
@@ -3,12 +3,12 @@ package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html import be.simplenotes.app.extensions.html
import be.simplenotes.app.extensions.redirect import be.simplenotes.app.extensions.redirect
import be.simplenotes.app.utils.parseSearchTerms import be.simplenotes.app.utils.parseSearchTerms
import be.simplenotes.app.views.NoteView import be.simplenotes.views.NoteView
import be.simplenotes.domain.security.JwtPayload
import be.simplenotes.domain.usecases.NoteService import be.simplenotes.domain.usecases.NoteService
import be.simplenotes.domain.usecases.markdown.InvalidMeta import be.simplenotes.domain.usecases.markdown.InvalidMeta
import be.simplenotes.domain.usecases.markdown.MissingMeta import be.simplenotes.domain.usecases.markdown.MissingMeta
import be.simplenotes.domain.usecases.markdown.ValidationError import be.simplenotes.domain.usecases.markdown.ValidationError
import be.simplenotes.types.LoggedInUser
import org.http4k.core.Method import org.http4k.core.Method
import org.http4k.core.Request import org.http4k.core.Request
import org.http4k.core.Response import org.http4k.core.Response
@@ -25,18 +25,18 @@ class NoteController(
private val noteService: NoteService, private val noteService: NoteService,
) { ) {
fun new(request: Request, jwtPayload: JwtPayload): Response { fun new(request: Request, loggedInUser: LoggedInUser): Response {
if (request.method == Method.GET) return Response(OK).html(view.noteEditor(jwtPayload)) if (request.method == Method.GET) return Response(OK).html(view.noteEditor(loggedInUser))
val markdownForm = request.form("markdown") ?: "" val markdownForm = request.form("markdown") ?: ""
return noteService.create(jwtPayload.userId, markdownForm).fold( return noteService.create(loggedInUser.userId, markdownForm).fold(
{ {
val html = when (it) { val html = when (it) {
MissingMeta -> view.noteEditor(jwtPayload, error = "Missing note metadata", textarea = markdownForm) MissingMeta -> view.noteEditor(loggedInUser, error = "Missing note metadata", textarea = markdownForm)
InvalidMeta -> view.noteEditor(jwtPayload, error = "Invalid note metadata", textarea = markdownForm) InvalidMeta -> view.noteEditor(loggedInUser, error = "Invalid note metadata", textarea = markdownForm)
is ValidationError -> view.noteEditor( is ValidationError -> view.noteEditor(
jwtPayload, loggedInUser,
validationErrors = it.validationErrors, validationErrors = it.validationErrors,
textarea = markdownForm textarea = markdownForm
) )
@@ -49,66 +49,66 @@ class NoteController(
) )
} }
fun list(request: Request, jwtPayload: JwtPayload): Response { fun list(request: Request, loggedInUser: LoggedInUser): Response {
val currentPage = request.query("page")?.toIntOrNull()?.let(::abs) ?: 1 val currentPage = request.query("page")?.toIntOrNull()?.let(::abs) ?: 1
val tag = request.query("tag") val tag = request.query("tag")
val (pages, notes) = noteService.paginatedNotes(jwtPayload.userId, currentPage, tag = tag) val (pages, notes) = noteService.paginatedNotes(loggedInUser.userId, currentPage, tag = tag)
val deletedCount = noteService.countDeleted(jwtPayload.userId) val deletedCount = noteService.countDeleted(loggedInUser.userId)
return Response(OK).html(view.notes(jwtPayload, notes, currentPage, pages, deletedCount, tag = tag)) return Response(OK).html(view.notes(loggedInUser, notes, currentPage, pages, deletedCount, tag = tag))
} }
fun search(request: Request, jwtPayload: JwtPayload): Response { fun search(request: Request, loggedInUser: LoggedInUser): Response {
val query = request.form("search") ?: "" val query = request.form("search") ?: ""
val terms = parseSearchTerms(query) val terms = parseSearchTerms(query)
val notes = noteService.search(jwtPayload.userId, terms) val notes = noteService.search(loggedInUser.userId, terms)
val deletedCount = noteService.countDeleted(jwtPayload.userId) val deletedCount = noteService.countDeleted(loggedInUser.userId)
return Response(OK).html(view.search(jwtPayload, notes, query, deletedCount)) return Response(OK).html(view.search(loggedInUser, notes, query, deletedCount))
} }
fun note(request: Request, jwtPayload: JwtPayload): Response { fun note(request: Request, loggedInUser: LoggedInUser): Response {
val noteUuid = request.uuidPath() ?: return Response(NOT_FOUND) val noteUuid = request.uuidPath() ?: return Response(NOT_FOUND)
if (request.method == Method.POST) { if (request.method == Method.POST) {
if (request.form("delete") != null) { if (request.form("delete") != null) {
return if (noteService.trash(jwtPayload.userId, noteUuid)) return if (noteService.trash(loggedInUser.userId, noteUuid))
Response.redirect("/notes") // TODO: flash cookie to show success ? Response.redirect("/notes") // TODO: flash cookie to show success ?
else else
Response(NOT_FOUND) // TODO: show an error Response(NOT_FOUND) // TODO: show an error
} }
if (request.form("public") != null) { if (request.form("public") != null) {
if (!noteService.makePublic(jwtPayload.userId, noteUuid)) return Response(NOT_FOUND) if (!noteService.makePublic(loggedInUser.userId, noteUuid)) return Response(NOT_FOUND)
} else if (request.form("private") != null) { } else if (request.form("private") != null) {
if (!noteService.makePrivate(jwtPayload.userId, noteUuid)) return Response(NOT_FOUND) if (!noteService.makePrivate(loggedInUser.userId, noteUuid)) return Response(NOT_FOUND)
} }
} }
val note = noteService.find(jwtPayload.userId, noteUuid) ?: return Response(NOT_FOUND) val note = noteService.find(loggedInUser.userId, noteUuid) ?: return Response(NOT_FOUND)
return Response(OK).html(view.renderedNote(jwtPayload, note, shared = false)) return Response(OK).html(view.renderedNote(loggedInUser, note, shared = false))
} }
fun public(request: Request, jwtPayload: JwtPayload?): Response { fun public(request: Request, loggedInUser: LoggedInUser?): Response {
val noteUuid = request.uuidPath() ?: return Response(NOT_FOUND) val noteUuid = request.uuidPath() ?: return Response(NOT_FOUND)
val note = noteService.findPublic(noteUuid) ?: return Response(NOT_FOUND) val note = noteService.findPublic(noteUuid) ?: return Response(NOT_FOUND)
return Response(OK).html(view.renderedNote(jwtPayload, note, shared = true)) return Response(OK).html(view.renderedNote(loggedInUser, note, shared = true))
} }
fun edit(request: Request, jwtPayload: JwtPayload): Response { fun edit(request: Request, loggedInUser: LoggedInUser): Response {
val noteUuid = request.uuidPath() ?: return Response(NOT_FOUND) val noteUuid = request.uuidPath() ?: return Response(NOT_FOUND)
val note = noteService.find(jwtPayload.userId, noteUuid) ?: return Response(NOT_FOUND) val note = noteService.find(loggedInUser.userId, noteUuid) ?: return Response(NOT_FOUND)
if (request.method == Method.GET) { if (request.method == Method.GET) {
return Response(OK).html(view.noteEditor(jwtPayload, textarea = note.markdown)) return Response(OK).html(view.noteEditor(loggedInUser, textarea = note.markdown))
} }
val markdownForm = request.form("markdown") ?: "" val markdownForm = request.form("markdown") ?: ""
return noteService.update(jwtPayload.userId, note.uuid, markdownForm).fold( return noteService.update(loggedInUser.userId, note.uuid, markdownForm).fold(
{ {
val html = when (it) { val html = when (it) {
MissingMeta -> view.noteEditor(jwtPayload, error = "Missing note metadata", textarea = markdownForm) MissingMeta -> view.noteEditor(loggedInUser, error = "Missing note metadata", textarea = markdownForm)
InvalidMeta -> view.noteEditor(jwtPayload, error = "Invalid note metadata", textarea = markdownForm) InvalidMeta -> view.noteEditor(loggedInUser, error = "Invalid note metadata", textarea = markdownForm)
is ValidationError -> view.noteEditor( is ValidationError -> view.noteEditor(
jwtPayload, loggedInUser,
validationErrors = it.validationErrors, validationErrors = it.validationErrors,
textarea = markdownForm textarea = markdownForm
) )
@@ -121,21 +121,21 @@ class NoteController(
) )
} }
fun trash(request: Request, jwtPayload: JwtPayload): Response { fun trash(request: Request, loggedInUser: LoggedInUser): Response {
val currentPage = request.query("page")?.toIntOrNull()?.let(::abs) ?: 1 val currentPage = request.query("page")?.toIntOrNull()?.let(::abs) ?: 1
val tag = request.query("tag") val tag = request.query("tag")
val (pages, notes) = noteService.paginatedNotes(jwtPayload.userId, currentPage, tag = tag, deleted = true) val (pages, notes) = noteService.paginatedNotes(loggedInUser.userId, currentPage, tag = tag, deleted = true)
return Response(OK).html(view.trash(jwtPayload, notes, currentPage, pages)) return Response(OK).html(view.trash(loggedInUser, notes, currentPage, pages))
} }
fun deleted(request: Request, jwtPayload: JwtPayload): Response { fun deleted(request: Request, loggedInUser: LoggedInUser): Response {
val uuid = request.uuidPath() ?: return Response(NOT_FOUND) val uuid = request.uuidPath() ?: return Response(NOT_FOUND)
return if (request.form("delete") != null) return if (request.form("delete") != null)
if (noteService.delete(jwtPayload.userId, uuid)) if (noteService.delete(loggedInUser.userId, uuid))
Response.redirect("/notes/trash") Response.redirect("/notes/trash")
else else
Response(NOT_FOUND) Response(NOT_FOUND)
else if (noteService.restore(jwtPayload.userId, uuid)) else if (noteService.restore(loggedInUser.userId, uuid))
Response.redirect("/notes/$uuid") Response.redirect("/notes/$uuid")
else else
Response(NOT_FOUND) Response(NOT_FOUND)
@@ -2,11 +2,11 @@ package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html import be.simplenotes.app.extensions.html
import be.simplenotes.app.extensions.redirect import be.simplenotes.app.extensions.redirect
import be.simplenotes.app.views.SettingView import be.simplenotes.views.SettingView
import be.simplenotes.domain.security.JwtPayload
import be.simplenotes.domain.usecases.UserService import be.simplenotes.domain.usecases.UserService
import be.simplenotes.domain.usecases.users.delete.DeleteError import be.simplenotes.domain.usecases.users.delete.DeleteError
import be.simplenotes.domain.usecases.users.delete.DeleteForm import be.simplenotes.domain.usecases.users.delete.DeleteForm
import be.simplenotes.types.LoggedInUser
import org.http4k.core.* import org.http4k.core.*
import org.http4k.core.body.form import org.http4k.core.body.form
import org.http4k.core.cookie.invalidateCookie import org.http4k.core.cookie.invalidateCookie
@@ -15,11 +15,11 @@ class SettingsController(
private val userService: UserService, private val userService: UserService,
private val settingView: SettingView, private val settingView: SettingView,
) { ) {
fun settings(request: Request, jwtPayload: JwtPayload): Response { fun settings(request: Request, loggedInUser: LoggedInUser): Response {
if (request.method == Method.GET) if (request.method == Method.GET)
return Response(Status.OK).html(settingView.settings(jwtPayload)) return Response(Status.OK).html(settingView.settings(loggedInUser))
val deleteForm = request.deleteForm(jwtPayload) val deleteForm = request.deleteForm(loggedInUser)
val result = userService.delete(deleteForm) val result = userService.delete(deleteForm)
return result.fold( return result.fold(
@@ -28,13 +28,13 @@ class SettingsController(
DeleteError.Unregistered -> Response.redirect("/").invalidateCookie("Bearer") DeleteError.Unregistered -> Response.redirect("/").invalidateCookie("Bearer")
DeleteError.WrongPassword -> Response(Status.OK).html( DeleteError.WrongPassword -> Response(Status.OK).html(
settingView.settings( settingView.settings(
jwtPayload, loggedInUser,
error = "Wrong password" error = "Wrong password"
) )
) )
is DeleteError.InvalidForm -> Response(Status.OK).html( is DeleteError.InvalidForm -> Response(Status.OK).html(
settingView.settings( settingView.settings(
jwtPayload, loggedInUser,
validationErrors = it.validationErrors validationErrors = it.validationErrors
) )
) )
@@ -53,23 +53,23 @@ class SettingsController(
.header("Content-Type", contentType) .header("Content-Type", contentType)
} }
fun export(request: Request, jwtPayload: JwtPayload): Response { fun export(request: Request, loggedInUser: LoggedInUser): Response {
val isDownload = request.form("download") != null val isDownload = request.form("download") != null
return if (isDownload) { return if (isDownload) {
val filename = "simplenotes-export-${jwtPayload.username}" val filename = "simplenotes-export-${loggedInUser.username}"
if (request.form("format") == "zip") { if (request.form("format") == "zip") {
val zip = userService.exportAsZip(jwtPayload.userId) val zip = userService.exportAsZip(loggedInUser.userId)
Response(Status.OK) Response(Status.OK)
.with(attachment("$filename.zip", "application/zip")) .with(attachment("$filename.zip", "application/zip"))
.body(zip) .body(zip)
} else } else
Response(Status.OK) Response(Status.OK)
.with(attachment("$filename.json", "application/json")) .with(attachment("$filename.json", "application/json"))
.body(userService.exportAsJson(jwtPayload.userId)) .body(userService.exportAsJson(loggedInUser.userId))
} else Response(Status.OK).body(userService.exportAsJson(jwtPayload.userId)).header("Content-Type", "application/json") } else Response(Status.OK).body(userService.exportAsJson(loggedInUser.userId)).header("Content-Type", "application/json")
} }
private fun Request.deleteForm(jwtPayload: JwtPayload) = private fun Request.deleteForm(loggedInUser: LoggedInUser) =
DeleteForm(jwtPayload.username, form("password"), form("checked") != null) DeleteForm(loggedInUser.username, form("password"), form("checked") != null)
} }
@@ -3,14 +3,14 @@ package be.simplenotes.app.controllers
import be.simplenotes.app.extensions.html import be.simplenotes.app.extensions.html
import be.simplenotes.app.extensions.isSecure import be.simplenotes.app.extensions.isSecure
import be.simplenotes.app.extensions.redirect import be.simplenotes.app.extensions.redirect
import be.simplenotes.app.views.UserView import be.simplenotes.views.UserView
import be.simplenotes.domain.security.JwtPayload
import be.simplenotes.domain.usecases.UserService import be.simplenotes.domain.usecases.UserService
import be.simplenotes.domain.usecases.users.login.* import be.simplenotes.domain.usecases.users.login.*
import be.simplenotes.domain.usecases.users.register.InvalidRegisterForm import be.simplenotes.domain.usecases.users.register.InvalidRegisterForm
import be.simplenotes.domain.usecases.users.register.RegisterForm import be.simplenotes.domain.usecases.users.register.RegisterForm
import be.simplenotes.domain.usecases.users.register.UserExists import be.simplenotes.domain.usecases.users.register.UserExists
import be.simplenotes.shared.config.JwtConfig import be.simplenotes.config.JwtConfig
import be.simplenotes.types.LoggedInUser
import org.http4k.core.Method.GET import org.http4k.core.Method.GET
import org.http4k.core.Request import org.http4k.core.Request
import org.http4k.core.Response import org.http4k.core.Response
@@ -27,9 +27,9 @@ class UserController(
private val userView: UserView, private val userView: UserView,
private val jwtConfig: JwtConfig, private val jwtConfig: JwtConfig,
) { ) {
fun register(request: Request, jwtPayload: JwtPayload?): Response { fun register(request: Request, loggedInUser: LoggedInUser?): Response {
if (request.method == GET) return Response(OK).html( if (request.method == GET) return Response(OK).html(
userView.register(jwtPayload) userView.register(loggedInUser)
) )
val result = userService.register(request.registerForm()) val result = userService.register(request.registerForm())
@@ -38,12 +38,12 @@ class UserController(
{ {
val html = when (it) { val html = when (it) {
UserExists -> userView.register( UserExists -> userView.register(
jwtPayload, loggedInUser,
error = "User already exists" error = "User already exists"
) )
is InvalidRegisterForm -> is InvalidRegisterForm ->
userView.register( userView.register(
jwtPayload, loggedInUser,
validationErrors = it.validationErrors validationErrors = it.validationErrors
) )
} }
@@ -58,9 +58,9 @@ class UserController(
private fun Request.registerForm() = RegisterForm(form("username"), form("password")) private fun Request.registerForm() = RegisterForm(form("username"), form("password"))
private fun Request.loginForm(): LoginForm = registerForm() private fun Request.loginForm(): LoginForm = registerForm()
fun login(request: Request, jwtPayload: JwtPayload?): Response { fun login(request: Request, loggedInUser: LoggedInUser?): Response {
if (request.method == GET) return Response(OK).html( if (request.method == GET) return Response(OK).html(
userView.login(jwtPayload) userView.login(loggedInUser)
) )
val result = userService.login(request.loginForm()) val result = userService.login(request.loginForm())
@@ -70,17 +70,17 @@ class UserController(
val html = when (it) { val html = when (it) {
Unregistered -> Unregistered ->
userView.login( userView.login(
jwtPayload, loggedInUser,
error = "User does not exist" error = "User does not exist"
) )
WrongPassword -> WrongPassword ->
userView.login( userView.login(
jwtPayload, loggedInUser,
error = "Wrong password" error = "Wrong password"
) )
is InvalidLoginForm -> is InvalidLoginForm ->
userView.login( userView.login(
jwtPayload, loggedInUser,
validationErrors = it.validationErrors validationErrors = it.validationErrors
) )
} }
@@ -0,0 +1,35 @@
package be.simplenotes.app.extensions
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.http4k.asString
import org.http4k.core.Body
import org.http4k.core.ContentType
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.FOUND
import org.http4k.core.Status.Companion.MOVED_PERMANENTLY
import org.http4k.lens.*
fun Response.html(html: String) = body(html)
.header("Content-Type", "text/html; charset=utf-8")
.header("Cache-Control", "no-cache")
fun Response.Companion.redirect(url: String, permanent: Boolean = false) =
Response(if (permanent) MOVED_PERMANENTLY else FOUND).header("Location", url)
fun Request.isSecure() = header("X-Forwarded-Proto")?.contains("https") ?: false
val bodyLens = httpBodyRoot(
listOf(Meta(true, "body", ParamMeta.ObjectParam, "body")),
ContentType.APPLICATION_JSON.withNoDirectives(), ContentNegotiation.StrictNoDirective
).map(
{ it.payload.asString() },
{ Body(it) }
)
inline fun <reified T> Json.auto(): BiDiBodyLensSpec<T> = bodyLens.map(
{ decodeFromString(it) },
{ encodeToString(it) }
)
@@ -1,8 +1,8 @@
package be.simplenotes.app.filters package be.simplenotes.app.filters
import be.simplenotes.app.extensions.redirect import be.simplenotes.app.extensions.redirect
import be.simplenotes.domain.security.JwtPayload
import be.simplenotes.domain.security.JwtPayloadExtractor import be.simplenotes.domain.security.JwtPayloadExtractor
import be.simplenotes.types.LoggedInUser
import org.http4k.core.* import org.http4k.core.*
import org.http4k.core.Status.Companion.UNAUTHORIZED import org.http4k.core.Status.Companion.UNAUTHORIZED
import org.http4k.core.cookie.cookie import org.http4k.core.cookie.cookie
@@ -40,7 +40,7 @@ class AuthFilter(
} }
} }
fun Request.jwtPayload(ctx: RequestContexts): JwtPayload? = ctx[this][authKey] fun Request.jwtPayload(ctx: RequestContexts): LoggedInUser? = ctx[this][authKey]
enum class JwtSource { enum class JwtSource {
Header, Cookie Header, Cookie
@@ -1,8 +1,8 @@
package be.simplenotes.app.filters package be.simplenotes.app.filters
import be.simplenotes.app.extensions.html import be.simplenotes.app.extensions.html
import be.simplenotes.app.views.ErrorView import be.simplenotes.views.ErrorView
import be.simplenotes.app.views.ErrorView.Type.* import be.simplenotes.views.ErrorView.Type.*
import org.http4k.core.* import org.http4k.core.*
import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR
import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.NOT_FOUND
@@ -0,0 +1,40 @@
package be.simplenotes.app.jetty
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.servlet.ServletContextHandler.SESSIONS
import org.eclipse.jetty.servlet.ServletHolder
import org.http4k.core.HttpHandler
import org.http4k.server.Http4kServer
import org.http4k.server.ServerConfig
import org.http4k.servlet.asServlet
class Jetty(private val port: Int, private val server: Server) : ServerConfig {
constructor(port: Int = 8000) : this(port, http(port))
constructor(port: Int, vararg inConnectors: ConnectorBuilder) : this(port, Server().apply {
inConnectors.forEach { addConnector(it(this)) }
})
override fun toServer(httpHandler: HttpHandler): Http4kServer {
server.insertHandler(httpHandler.toJettyHandler())
return object : Http4kServer {
override fun start(): Http4kServer = apply {
server.start()
}
override fun stop(): Http4kServer = apply { server.stop() }
override fun port(): Int = if (port > 0) port else server.uri.port
}
}
}
fun HttpHandler.toJettyHandler() = ServletContextHandler(SESSIONS).apply {
addServlet(ServletHolder(this@toJettyHandler.asServlet()), "/*")
}
typealias ConnectorBuilder = (Server) -> ServerConnector
fun http(httpPort: Int): ConnectorBuilder = { server: Server -> ServerConnector(server).apply { port = httpPort } }
@@ -0,0 +1,12 @@
package be.simplenotes.app.modules
import be.simplenotes.app.controllers.*
import org.koin.dsl.module
val controllerModule = module {
single { UserController(get(), get(), get()) }
single { HealthCheckController(get()) }
single { BaseController(get()) }
single { NoteController(get(), get()) }
single { SettingsController(get(), get()) }
}
@@ -5,17 +5,17 @@ import be.simplenotes.app.filters.AuthFilter
import be.simplenotes.app.filters.AuthType import be.simplenotes.app.filters.AuthType
import be.simplenotes.app.filters.ErrorFilter import be.simplenotes.app.filters.ErrorFilter
import be.simplenotes.app.filters.TransactionFilter import be.simplenotes.app.filters.TransactionFilter
import be.simplenotes.app.jetty.ConnectorBuilder
import be.simplenotes.app.jetty.Jetty
import be.simplenotes.app.routes.Router import be.simplenotes.app.routes.Router
import be.simplenotes.app.utils.StaticFileResolver import be.simplenotes.app.utils.StaticFileResolver
import be.simplenotes.app.utils.StaticFileResolverImpl import be.simplenotes.app.utils.StaticFileResolverImpl
import be.simplenotes.app.views.ErrorView import be.simplenotes.views.ErrorView
import be.simplenotes.shared.config.ServerConfig import be.simplenotes.config.ServerConfig
import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.ServerConnector
import org.http4k.core.Filter import org.http4k.core.Filter
import org.http4k.core.RequestContexts import org.http4k.core.RequestContexts
import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.RoutingHttpHandler
import org.http4k.server.ConnectorBuilder
import org.http4k.server.Jetty
import org.http4k.server.asServer import org.http4k.server.asServer
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.core.qualifier.qualifier import org.koin.core.qualifier.qualifier
@@ -59,5 +59,5 @@ val serverModule = module {
single<Filter>(AuthType.Required.qualifier) { AuthFilter(get(), AuthType.Required, get()) } single<Filter>(AuthType.Required.qualifier) { AuthFilter(get(), AuthType.Required, get()) }
single { ErrorFilter(get()) } single { ErrorFilter(get()) }
single { TransactionFilter(get()) } single { TransactionFilter(get()) }
single { ErrorView(get()) } single(named("styles")) { get<StaticFileResolver>().resolve("styles.css") }
} }
@@ -4,7 +4,7 @@ import be.simplenotes.app.api.ApiNoteController
import be.simplenotes.app.api.ApiUserController import be.simplenotes.app.api.ApiUserController
import be.simplenotes.app.controllers.* import be.simplenotes.app.controllers.*
import be.simplenotes.app.filters.* import be.simplenotes.app.filters.*
import be.simplenotes.domain.security.JwtPayload import be.simplenotes.types.LoggedInUser
import org.http4k.core.* import org.http4k.core.*
import org.http4k.core.Method.* import org.http4k.core.Method.*
import org.http4k.filter.ResponseFilters.GZip import org.http4k.filter.ResponseFilters.GZip
@@ -102,5 +102,5 @@ class Router(
this to transactionFilter.then { handler(it, it.jwtPayload(contexts)) } this to transactionFilter.then { handler(it, it.jwtPayload(contexts)) }
} }
private typealias PublicHandler = (Request, JwtPayload?) -> Response private typealias PublicHandler = (Request, LoggedInUser?) -> Response
private typealias ProtectedHandler = (Request, JwtPayload) -> Response private typealias ProtectedHandler = (Request, LoggedInUser) -> Response
@@ -1,6 +1,6 @@
package be.simplenotes.app.utils package be.simplenotes.app.utils
import be.simplenotes.domain.usecases.search.SearchTerms import be.simplenotes.search.SearchTerms
private fun innerRegex(name: String) = private fun innerRegex(name: String) =
"""$name:['"](.*?)['"]""".toRegex() """$name:['"](.*?)['"]""".toRegex()

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Before

Width:  |  Height:  |  Size: 814 B

After

Width:  |  Height:  |  Size: 814 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -1,9 +1,9 @@
package be.simplenotes.app.filters package be.simplenotes.app.filters
import be.simplenotes.domain.security.JwtPayload
import be.simplenotes.domain.security.JwtPayloadExtractor import be.simplenotes.domain.security.JwtPayloadExtractor
import be.simplenotes.domain.security.SimpleJwt import be.simplenotes.domain.security.SimpleJwt
import be.simplenotes.shared.config.JwtConfig import be.simplenotes.config.JwtConfig
import be.simplenotes.types.LoggedInUser
import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.assertion.assertThat
import org.http4k.core.* import org.http4k.core.*
import org.http4k.core.Method.GET import org.http4k.core.Method.GET
@@ -58,7 +58,7 @@ internal class AuthFilterTest {
@Test @Test
fun `it should allow a valid token`() { fun `it should allow a valid token`() {
val jwtPayload = JwtPayload(1, "user") val jwtPayload = LoggedInUser(1, "user")
val token = simpleJwt.sign(jwtPayload) val token = simpleJwt.sign(jwtPayload)
val response = app(Request(GET, "/optional").cookie("Bearer", token)) val response = app(Request(GET, "/optional").cookie("Bearer", token))
assertThat(response, hasStatus(OK)) assertThat(response, hasStatus(OK))
@@ -84,7 +84,7 @@ internal class AuthFilterTest {
@Test @Test
fun `it should allow a valid token"`() { fun `it should allow a valid token"`() {
val jwtPayload = JwtPayload(1, "user") val jwtPayload = LoggedInUser(1, "user")
val token = simpleJwt.sign(jwtPayload) val token = simpleJwt.sign(jwtPayload)
val response = app(Request(GET, "/protected").cookie("Bearer", token)) val response = app(Request(GET, "/protected").cookie("Bearer", token))
assertThat(response, hasStatus(OK)) assertThat(response, hasStatus(OK))
@@ -1,6 +1,6 @@
package be.simplenotes.app.utils package be.simplenotes.app.utils
import be.simplenotes.domain.usecases.search.SearchTerms import be.simplenotes.search.SearchTerms
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.MethodSource
+9
View File
@@ -0,0 +1,9 @@
import be.simplenotes.Libs
plugins {
id("be.simplenotes.base")
}
dependencies {
implementation(Libs.koinCore)
}
@@ -1,4 +1,4 @@
package be.simplenotes.shared.config package be.simplenotes.config
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -1,12 +1,9 @@
package be.simplenotes.app package be.simplenotes.config
import be.simplenotes.shared.config.DataSourceConfig
import be.simplenotes.shared.config.JwtConfig
import be.simplenotes.shared.config.ServerConfig
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class Config { class ConfigLoader {
//region Config loading //region Config loading
private val properties: Properties = javaClass private val properties: Properties = javaClass
.getResource("/application.properties") .getResource("/application.properties")
@@ -0,0 +1,10 @@
package be.simplenotes.config
import org.koin.dsl.module
val configModule = module {
single { ConfigLoader() }
single { get<ConfigLoader>().dataSourceConfig }
single { get<ConfigLoader>().jwtConfig }
single { get<ConfigLoader>().serverConfig }
}
+29
View File
@@ -0,0 +1,29 @@
import be.simplenotes.Libs
plugins {
id("be.simplenotes.base")
kotlin("plugin.serialization") version "1.4.10"
}
dependencies {
implementation(project(":simplenotes-config"))
implementation(project(":simplenotes-types"))
implementation(project(":simplenotes-persistance"))
implementation(project(":simplenotes-search"))
implementation(Libs.kotlinxSerializationJson)
implementation(Libs.koinCore)
implementation(Libs.arrowCore)
implementation(Libs.konform)
implementation(Libs.jbcrypt)
implementation(Libs.javaJwt)
implementation(Libs.flexmark)
implementation(Libs.flexmarkGfmTasklist)
implementation(Libs.snakeyaml)
implementation(Libs.owaspHtmlSanitizer)
implementation(Libs.commonsCompress)
testImplementation(Libs.hamkrest)
testImplementation(Libs.junit)
testImplementation(Libs.mockk)
}
@@ -2,7 +2,7 @@ package be.simplenotes.domain.security
import org.owasp.html.HtmlPolicyBuilder import org.owasp.html.HtmlPolicyBuilder
object HtmlSanitizer { internal object HtmlSanitizer {
private val htmlPolicy = HtmlPolicyBuilder() private val htmlPolicy = HtmlPolicyBuilder()
.allowElements("a") .allowElements("a")
.allowCommonBlockElements() .allowCommonBlockElements()
@@ -1,18 +1,14 @@
package be.simplenotes.domain.security package be.simplenotes.domain.security
import be.simplenotes.domain.model.PersistedUser import be.simplenotes.types.LoggedInUser
import com.auth0.jwt.exceptions.JWTVerificationException import com.auth0.jwt.exceptions.JWTVerificationException
data class JwtPayload(val userId: Int, val username: String) {
constructor(user: PersistedUser) : this(user.id, user.username)
}
class JwtPayloadExtractor(private val jwt: SimpleJwt) { class JwtPayloadExtractor(private val jwt: SimpleJwt) {
operator fun invoke(token: String): JwtPayload? = try { operator fun invoke(token: String): LoggedInUser? = try {
val decodedJWT = jwt.verifier.verify(token) val decodedJWT = jwt.verifier.verify(token)
val id = decodedJWT.getClaim(userIdField).asInt() ?: null val id = decodedJWT.getClaim(userIdField).asInt() ?: null
val username = decodedJWT.getClaim(usernameField).asString() ?: null val username = decodedJWT.getClaim(usernameField).asString() ?: null
id?.let { username?.let { JwtPayload(id, username) } } id?.let { username?.let { LoggedInUser(id, username) } }
} catch (e: JWTVerificationException) { } catch (e: JWTVerificationException) {
null null
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
@@ -1,6 +1,7 @@
package be.simplenotes.domain.security package be.simplenotes.domain.security
import be.simplenotes.shared.config.JwtConfig import be.simplenotes.config.JwtConfig
import be.simplenotes.types.LoggedInUser
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
@@ -15,9 +16,9 @@ class SimpleJwt(jwtConfig: JwtConfig) {
private val algorithm = Algorithm.HMAC256(jwtConfig.secret) private val algorithm = Algorithm.HMAC256(jwtConfig.secret)
val verifier: JWTVerifier = JWT.require(algorithm).build() val verifier: JWTVerifier = JWT.require(algorithm).build()
fun sign(jwtPayload: JwtPayload): String = JWT.create() fun sign(loggedInUser: LoggedInUser): String = JWT.create()
.withClaim(userIdField, jwtPayload.userId) .withClaim(userIdField, loggedInUser.userId)
.withClaim(usernameField, jwtPayload.username) .withClaim(usernameField, loggedInUser.username)
.withExpiresAt(getExpiration()) .withExpiresAt(getExpiration())
.sign(algorithm) .sign(algorithm)
@@ -1,17 +1,16 @@
package be.simplenotes.domain.usecases package be.simplenotes.domain.usecases
import arrow.core.Either import arrow.core.computations.either
import arrow.core.extensions.fx
import be.simplenotes.domain.model.Note
import be.simplenotes.domain.model.PersistedNote
import be.simplenotes.domain.model.PersistedNoteMetadata
import be.simplenotes.domain.security.HtmlSanitizer import be.simplenotes.domain.security.HtmlSanitizer
import be.simplenotes.domain.usecases.markdown.MarkdownConverter import be.simplenotes.domain.usecases.markdown.MarkdownConverter
import be.simplenotes.domain.usecases.markdown.MarkdownParsingError import be.simplenotes.domain.usecases.markdown.MarkdownParsingError
import be.simplenotes.domain.usecases.repositories.NoteRepository import be.simplenotes.persistance.repositories.NoteRepository
import be.simplenotes.domain.usecases.repositories.UserRepository import be.simplenotes.persistance.repositories.UserRepository
import be.simplenotes.domain.usecases.search.NoteSearcher import be.simplenotes.search.NoteSearcher
import be.simplenotes.domain.usecases.search.SearchTerms import be.simplenotes.search.SearchTerms
import be.simplenotes.types.Note
import be.simplenotes.types.PersistedNote
import be.simplenotes.types.PersistedNoteMetadata
import java.util.* import java.util.*
class NoteService( class NoteService(
@@ -21,7 +20,7 @@ class NoteService(
private val searcher: NoteSearcher, private val searcher: NoteSearcher,
) { ) {
fun create(userId: Int, markdownText: String) = Either.fx<MarkdownParsingError, PersistedNote> { fun create(userId: Int, markdownText: String) = either.eager<MarkdownParsingError, PersistedNote> {
val persistedNote = !markdownConverter.renderDocument(markdownText) val persistedNote = !markdownConverter.renderDocument(markdownText)
.map { it.copy(html = HtmlSanitizer.sanitize(it.html)) } .map { it.copy(html = HtmlSanitizer.sanitize(it.html)) }
.map { Note(it.metadata, markdown = markdownText, html = it.html) } .map { Note(it.metadata, markdown = markdownText, html = it.html) }
@@ -31,7 +30,7 @@ class NoteService(
persistedNote persistedNote
} }
fun update(userId: Int, uuid: UUID, markdownText: String) = Either.fx<MarkdownParsingError, PersistedNote?> { fun update(userId: Int, uuid: UUID, markdownText: String) = either.eager<MarkdownParsingError, PersistedNote?> {
val persistedNote = !markdownConverter.renderDocument(markdownText) val persistedNote = !markdownConverter.renderDocument(markdownText)
.map { it.copy(html = HtmlSanitizer.sanitize(it.html)) } .map { it.copy(html = HtmlSanitizer.sanitize(it.html)) }
.map { Note(it.metadata, markdown = markdownText, html = it.html) } .map { Note(it.metadata, markdown = markdownText, html = it.html) }

Some files were not shown because too many files have changed in this diff Show More