Compare commits
2 Commits
cb76a3253d
...
ead1932d48
| Author | SHA1 | Date | |
|---|---|---|---|
| ead1932d48 | |||
| 4a7dcec363 |
@ -1,6 +1,6 @@
|
||||
package be.simplenotes.app.api
|
||||
|
||||
import be.simplenotes.app.extensions.json
|
||||
import be.simplenotes.app.extensions.auto
|
||||
import be.simplenotes.app.utils.parseSearchTerms
|
||||
import be.simplenotes.domain.model.PersistedNote
|
||||
import be.simplenotes.domain.model.PersistedNoteMetadata
|
||||
@ -8,48 +8,39 @@ 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 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, jwtPayload: JwtPayload): Response {
|
||||
val content = json.decodeFromString(NoteContent.serializer(), request.bodyString()).content
|
||||
val content = noteContentLens(request)
|
||||
return noteService.create(jwtPayload.userId, content).fold(
|
||||
{
|
||||
Response(BAD_REQUEST)
|
||||
},
|
||||
{
|
||||
Response(OK).json(json.encodeToString(UuidContent.serializer(), UuidContent(it.uuid)))
|
||||
}
|
||||
{ Response(BAD_REQUEST) },
|
||||
{ uuidContentLens(UuidContent(it.uuid), Response(OK)) }
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
return persistedNotesMetadataLens(notes, Response(OK))
|
||||
}
|
||||
|
||||
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)) }
|
||||
fun note(request: Request, jwtPayload: JwtPayload): Response =
|
||||
noteService.find(jwtPayload.userId, uuidLens(request))
|
||||
?.let { persistedNoteLens(it, Response(OK)) }
|
||||
?: 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({
|
||||
val content = noteContentLens(request)
|
||||
return noteService.update(jwtPayload.userId, uuidLens(request), content).fold({
|
||||
Response(BAD_REQUEST)
|
||||
}, {
|
||||
if (it == null) Response(NOT_FOUND)
|
||||
@ -58,13 +49,19 @@ class ApiNoteController(private val noteService: NoteService, private val json:
|
||||
}
|
||||
|
||||
fun search(request: Request, jwtPayload: JwtPayload): Response {
|
||||
val query = json.decodeFromString(SearchContent.serializer(), request.bodyString()).query
|
||||
val query = searchContentLens(request)
|
||||
val terms = parseSearchTerms(query)
|
||||
val notes = noteService.search(jwtPayload.userId, terms)
|
||||
val json = json.encodeToString(ListSerializer(PersistedNoteMetadata.serializer()), notes)
|
||||
return Response(OK).json(json)
|
||||
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
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
package be.simplenotes.app.api
|
||||
|
||||
import be.simplenotes.app.extensions.json
|
||||
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
|
||||
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): 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)))
|
||||
})
|
||||
}
|
||||
fun login(request: Request) = userService
|
||||
.login(loginFormLens(request))
|
||||
.fold(
|
||||
{ Response(BAD_REQUEST) },
|
||||
{ tokenLens(Token(it), Response(OK)) }
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
||||
@ -1,17 +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.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
|
||||
|
||||
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) }
|
||||
)
|
||||
|
||||
@ -13,7 +13,7 @@ class BaseView(staticFileResolver: StaticFileResolver) : View(staticFileResolver
|
||||
) {
|
||||
section("text-center my-2 p-2") {
|
||||
h1("text-5xl casual") {
|
||||
span("text-teal-300") { +"Simplenotes " }
|
||||
span("text-teal-300") { +"SimpleNotes " }
|
||||
+"- access your notes anywhere"
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +143,6 @@ class NoteView(staticFileResolver: StaticFileResolver) : View(staticFileResolver
|
||||
}
|
||||
if (!shared) {
|
||||
noteActionForm(note)
|
||||
publicPrivateForm(note)
|
||||
|
||||
if (note.public) {
|
||||
p("my-4") {
|
||||
@ -169,8 +168,20 @@ class NoteView(staticFileResolver: StaticFileResolver) : View(staticFileResolver
|
||||
span("flex space-x-2 justify-end mb-4") {
|
||||
a(
|
||||
href = "/notes/${note.uuid}/edit",
|
||||
classes = "btn btn-teal"
|
||||
classes = "btn btn-green"
|
||||
) { +"Edit" }
|
||||
form(method = FormMethod.post, classes = "inline") {
|
||||
button(
|
||||
type = ButtonType.submit,
|
||||
name = if (note.public) "private" else "public",
|
||||
classes = "btn btn-teal"
|
||||
) {
|
||||
if (note.public)
|
||||
+"Private ?"
|
||||
else
|
||||
+"Public ?"
|
||||
}
|
||||
}
|
||||
form(method = FormMethod.post, classes = "inline") {
|
||||
button(
|
||||
type = ButtonType.submit,
|
||||
@ -180,22 +191,4 @@ class NoteView(staticFileResolver: StaticFileResolver) : View(staticFileResolver
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DIV.publicPrivateForm(note: PersistedNote) {
|
||||
span("flex space-x-2 justify-end mb-4") {
|
||||
|
||||
form(method = FormMethod.post, classes = "ml-auto ") {
|
||||
button(
|
||||
type = ButtonType.submit,
|
||||
name = if (note.public) "private" else "public",
|
||||
classes = "btn btn-teal"
|
||||
) {
|
||||
if (note.public)
|
||||
+"This note is public, do you want to make it private ?"
|
||||
else
|
||||
+"This note is private, do you want to make it public ?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,22 +26,23 @@ class SettingView(staticFileResolver: StaticFileResolver) : View(staticFileResol
|
||||
}
|
||||
}
|
||||
|
||||
section("m-4 p-4 bg-gray-800 rounded flex justify-around") {
|
||||
section("m-4 p-2 bg-gray-800 rounded flex flex-wrap justify-around items-end") {
|
||||
|
||||
form(method = FormMethod.post, action = "/export") {
|
||||
form(classes = "m-2", method = FormMethod.post, action = "/export") {
|
||||
button(name = "display",
|
||||
classes = "inline btn btn-teal block",
|
||||
type = submit) { +"Display my data" }
|
||||
}
|
||||
|
||||
form(method = FormMethod.post, action = "/export") {
|
||||
form(classes = "m-2", method = FormMethod.post, action = "/export") {
|
||||
|
||||
listOf("json", "zip").forEach { format ->
|
||||
div {
|
||||
div {
|
||||
listOf("json", "zip").forEach { format ->
|
||||
radioInput(name = "format") {
|
||||
id = format
|
||||
attributes["value"] = format
|
||||
if(format == "json") attributes["checked"] = ""
|
||||
if (format == "json") attributes["checked"] = ""
|
||||
else attributes["class"] = "ml-4"
|
||||
}
|
||||
label(classes = "ml-2") {
|
||||
attributes["for"] = format
|
||||
|
||||
@ -25,8 +25,8 @@ fun FlowContent.deletedNoteTable(notes: List<PersistedNoteMetadata>) = div("over
|
||||
td("text-center") { +updatedAt.toTimeAgo() }
|
||||
td { tags(tags) }
|
||||
td("text-center") {
|
||||
form(classes = "inline", method = post, action = "/notes/deleted/$uuid") {
|
||||
button(classes = "btn btn-red", type = submit, name = "delete") {
|
||||
form(method = post, action = "/notes/deleted/$uuid") {
|
||||
button(classes = "btn btn-red mb-2", type = submit, name = "delete") {
|
||||
+"Delete permanently"
|
||||
}
|
||||
button(classes = "ml-2 btn btn-green", type = submit, name = "restore") {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user