Compare commits
No commits in common. "0dfb2a7e03198e076182dafe10f74f15552870e5" and "ad97ba029e4c7c5831e07b1a02ca2c4b08660eb2" have entirely different histories.
0dfb2a7e03
...
ad97ba029e
@ -7,7 +7,10 @@ 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 org.http4k.core.*
|
import org.http4k.core.Method
|
||||||
|
import org.http4k.core.Request
|
||||||
|
import org.http4k.core.Response
|
||||||
|
import org.http4k.core.Status
|
||||||
import org.http4k.core.body.form
|
import org.http4k.core.body.form
|
||||||
import org.http4k.core.cookie.invalidateCookie
|
import org.http4k.core.cookie.invalidateCookie
|
||||||
|
|
||||||
@ -46,30 +49,19 @@ class SettingsController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachment(filename: String, contentType: String) = { response: Response ->
|
|
||||||
val name = filename.replace("[^a-zA-Z0-9-_.]".toRegex(), "_")
|
|
||||||
response
|
|
||||||
.header("Content-Disposition", "attachment; filename=\"$name\"")
|
|
||||||
.header("Content-Type", contentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun export(request: Request, jwtPayload: JwtPayload): Response {
|
fun export(request: Request, jwtPayload: JwtPayload): Response {
|
||||||
val isDownload = request.form("download") != null
|
val isDownload = request.form("download") != null
|
||||||
|
val json = userService.export(jwtPayload.userId)
|
||||||
return if (isDownload) {
|
val res = Response(Status.OK).body(json).header("Content-Type", "application/json")
|
||||||
val filename = "simplenotes-export-${jwtPayload.username}"
|
return if (isDownload) res.header(
|
||||||
if (request.form("format") == "zip") {
|
"Content-Disposition",
|
||||||
val zip = userService.exportAsZip(jwtPayload.userId)
|
"attachment; filename=\"simplenotes-export-${sanitizeFilename(jwtPayload.username)}.json\""
|
||||||
Response(Status.OK)
|
)
|
||||||
.with(attachment("$filename.zip", "application/zip"))
|
else res
|
||||||
.body(zip)
|
|
||||||
} else
|
|
||||||
Response(Status.OK)
|
|
||||||
.with(attachment("$filename.json", "application/json"))
|
|
||||||
.body(userService.exportAsJson(jwtPayload.userId))
|
|
||||||
} else Response(Status.OK).body(userService.exportAsJson(jwtPayload.userId)).header("Content-Type", "application/json")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sanitizeFilename(inputName: String): String = inputName.replace("[^a-zA-Z0-9-_.]".toRegex(), "_")
|
||||||
|
|
||||||
private fun Request.deleteForm(jwtPayload: JwtPayload) =
|
private fun Request.deleteForm(jwtPayload: JwtPayload) =
|
||||||
DeleteForm(jwtPayload.username, form("password"), form("checked") != null)
|
DeleteForm(jwtPayload.username, form("password"), form("checked") != null)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,31 +26,14 @@ class SettingView(staticFileResolver: StaticFileResolver) : View(staticFileResol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section("m-4 p-4 bg-gray-800 rounded flex justify-around") {
|
section("m-4 p-4 bg-gray-800 rounded") {
|
||||||
|
p(classes = "mb-4") {
|
||||||
form(method = FormMethod.post, action = "/export") {
|
+"Export all my data"
|
||||||
button(name = "display",
|
|
||||||
classes = "inline btn btn-teal block",
|
|
||||||
type = submit) { +"Display my data" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form(method = FormMethod.post, action = "/export") {
|
form(method = FormMethod.post, action = "/export") {
|
||||||
|
button(name = "display", classes = "inline btn btn-teal block", type = submit) { +"Display my data" }
|
||||||
listOf("json", "zip").forEach { format ->
|
button(name = "download", classes = "inline btn btn-green block ml-2 mt-2", type = submit) {
|
||||||
div {
|
|
||||||
radioInput(name = "format") {
|
|
||||||
id = format
|
|
||||||
attributes["value"] = format
|
|
||||||
if(format == "json") attributes["checked"] = ""
|
|
||||||
}
|
|
||||||
label(classes = "ml-2") {
|
|
||||||
attributes["for"] = format
|
|
||||||
+format
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button(name = "download", classes = "inline btn btn-green block mt-2", type = submit) {
|
|
||||||
+"Download my data"
|
+"Download my data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,11 +61,6 @@
|
|||||||
<groupId>org.jetbrains.kotlinx</groupId>
|
<groupId>org.jetbrains.kotlinx</groupId>
|
||||||
<artifactId>kotlinx-serialization-runtime</artifactId>
|
<artifactId>kotlinx-serialization-runtime</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-compress</artifactId>
|
|
||||||
<version>1.20</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
package be.simplenotes.domain.usecases.export
|
package be.simplenotes.domain.usecases.export
|
||||||
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface ExportUseCase {
|
interface ExportUseCase {
|
||||||
fun exportAsJson(userId: Int): String
|
fun export(userId: Int): String
|
||||||
fun exportAsZip(userId: Int): InputStream
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,15 +11,10 @@ import kotlinx.serialization.encoding.Decoder
|
|||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
|
||||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
internal class ExportUseCaseImpl(private val noteRepository: NoteRepository) : ExportUseCase {
|
internal class ExportUseCaseImpl(private val noteRepository: NoteRepository) : ExportUseCase {
|
||||||
override fun exportAsJson(userId: Int): String {
|
override fun export(userId: Int): String {
|
||||||
val module = SerializersModule {
|
val module = SerializersModule {
|
||||||
contextual(LocalDateTime::class, LocalDateTimeSerializer)
|
contextual(LocalDateTime::class, LocalDateTimeSerializer)
|
||||||
}
|
}
|
||||||
@ -32,40 +27,7 @@ internal class ExportUseCaseImpl(private val noteRepository: NoteRepository) : E
|
|||||||
val notes = noteRepository.export(userId)
|
val notes = noteRepository.export(userId)
|
||||||
return json.encodeToString(ListSerializer(ExportedNote.serializer()), notes)
|
return json.encodeToString(ListSerializer(ExportedNote.serializer()), notes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sanitizeFilename(inputName: String): String = inputName.replace("[^a-zA-Z0-9-_.]".toRegex(), "_")
|
|
||||||
|
|
||||||
override fun exportAsZip(userId: Int): InputStream {
|
|
||||||
val notes = noteRepository.export(userId)
|
|
||||||
val zipOutput = ZipOutput()
|
|
||||||
zipOutput.use { zip ->
|
|
||||||
notes.forEach {
|
|
||||||
val name = sanitizeFilename(it.title)
|
|
||||||
zip.write("notes/$name.md", it.markdown)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ByteArrayInputStream(zipOutput.outputStream.toByteArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ZipOutput : AutoCloseable {
|
|
||||||
val outputStream = ByteArrayOutputStream()
|
|
||||||
private val zipOutputStream = ZipArchiveOutputStream(outputStream)
|
|
||||||
|
|
||||||
fun write(path: String, content: String) {
|
|
||||||
val entry = ZipArchiveEntry(path)
|
|
||||||
zipOutputStream.putArchiveEntry(entry)
|
|
||||||
zipOutputStream.write(content.toByteArray())
|
|
||||||
zipOutputStream.closeArchiveEntry()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
zipOutputStream.finish()
|
|
||||||
zipOutputStream.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
|
internal object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
|
||||||
override val descriptor: SerialDescriptor
|
override val descriptor: SerialDescriptor
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user