Add backup

This commit is contained in:
Lars Westermann 2019-06-12 20:54:21 +02:00
parent 94c35b9067
commit 07a619f826
Signed by: lars.westermann
GPG key ID: 9D417FA5BB9D5E1D
6 changed files with 76 additions and 9 deletions

View file

@ -85,6 +85,7 @@
.board-running {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
&:empty + .board-running-empty {
display: block;

View file

@ -3,6 +3,7 @@ package de.kif.backend
import com.fasterxml.jackson.databind.SerializationFeature
import de.kif.backend.route.*
import de.kif.backend.route.api.*
import de.kif.backend.util.Backup
import de.kif.backend.util.pushService
import io.ktor.application.Application
import io.ktor.application.call
@ -101,4 +102,6 @@ fun Application.main() {
}
logger.info { "Responding at http://${Configuration.Server.host}:${Configuration.Server.port}$prefix/" }
Backup.startBackupService()
}

View file

@ -43,6 +43,7 @@ object Configuration {
val uploads by required<String>()
val database by required<String>()
val announcement by required<String>()
val backup by required<String>()
}
object Path {
@ -60,6 +61,9 @@ object Configuration {
val announcement by c(PathSpec.announcement)
val announcementPath: java.nio.file.Path by lazy { Paths.get(announcement).toAbsolutePath() }
val backup by c(PathSpec.backup)
val backupPath: java.nio.file.Path by lazy { Paths.get(backup).toAbsolutePath() }
}
private object ScheduleSpec : ConfigSpec("schedule") {
@ -93,6 +97,7 @@ object Configuration {
private object GeneralSpec : ConfigSpec("general") {
val allowedUploadExtensions by required<String>("allowed_upload_extensions")
val wikiUrl by required<String>("wiki_url")
val backupInterval by required<Long>("backup_interval")
}
object General {
@ -101,6 +106,7 @@ object Configuration {
allowedUploadExtensions.split(",").map { it.trim().toLowerCase() }.toSet()
}
val wikiUrl by c(GeneralSpec.wikiUrl)
val backupInterval by c(GeneralSpec.backupInterval)
}
private object TwitterSpec : ConfigSpec("twitter") {

View file

@ -83,11 +83,19 @@ object Resources {
Files.createDirectories(Configuration.Path.sessionsPath)
Files.createDirectories(Configuration.Path.uploadsPath)
Files.createDirectories(Configuration.Path.webPath)
Files.createDirectories(Configuration.Path.backupPath)
if (!Files.exists(Configuration.Path.announcementPath)) {
Files.createFile(Configuration.Path.announcementPath)
}
logger.info { "Database path: ${Configuration.Path.databasePath}" }
logger.info { "Sessions path: ${Configuration.Path.sessionsPath}" }
logger.info { "Uploads path: ${Configuration.Path.uploadsPath}" }
logger.info { "Web path: ${Configuration.Path.webPath}" }
logger.info { "Backup path: ${Configuration.Path.backupPath}" }
logger.info { "Announcement file: ${Configuration.Path.announcementPath}" }
logger.info { "Extract web content..." }
extractWeb()

View file

@ -1,11 +1,18 @@
package de.kif.backend.util
import de.kif.backend.Configuration
import de.kif.backend.database.Connection
import de.kif.backend.repository.*
import de.kif.common.RepositoryType
import de.kif.common.Serialization
import de.kif.common.model.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import mu.KotlinLogging
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.thread
@Serializable
data class Backup(
@ -17,6 +24,8 @@ data class Backup(
val workGroups: List<WorkGroup> = emptyList()
) {
companion object {
private val logger = KotlinLogging.logger {}
suspend fun backup(vararg repositories: RepositoryType): String {
var backup = Backup()
@ -78,7 +87,9 @@ data class Backup(
var workGroup = it
val track = workGroup.track
if (track != null) {
workGroup = workGroup.copy(track = track.copy(id = trackMap.firstOrNull { (i,_) -> i.equalsIgnoreId(track) }?.second ?: run {
workGroup = workGroup.copy(track = track.copy(id = trackMap.firstOrNull { (i, _) ->
i.equalsIgnoreId(track)
}?.second ?: run {
println("Cannot import work group, due to missing track")
return@mapNotNull null
}))
@ -94,14 +105,16 @@ data class Backup(
newSchedules.forEach {
ScheduleRepository.create(
it.copy(
room = it.room.copy(id = roomMap.firstOrNull { (i,_) -> i.equalsIgnoreId(it.room) }?.second ?: run {
println("Cannot import schedule, due to missing room")
return@forEach
}),
workGroup = it.workGroup.copy(id = workGroupMap.firstOrNull { (i,_) -> i.equalsIgnoreId(it.workGroup) }?.second ?: run {
println("Cannot import schedule, due to missing work group")
return@forEach
})
room = it.room.copy(
id = roomMap.firstOrNull { (i, _) -> i.equalsIgnoreId(it.room) }?.second ?: run {
println("Cannot import schedule, due to missing room")
return@forEach
}),
workGroup = it.workGroup.copy(
id = workGroupMap.firstOrNull { (i, _) -> i.equalsIgnoreId(it.workGroup) }?.second ?: run {
println("Cannot import schedule, due to missing work group")
return@forEach
})
)
)
@ -114,5 +127,39 @@ data class Backup(
import(data)
}
fun startBackupService() {
val backupPath = Configuration.Path.backupPath.toFile()
val backupInterval = Configuration.General.backupInterval
val formatter = SimpleDateFormat("yyyyMMdd'T'HHmmss")
thread(
start = true,
isDaemon = true,
name = "backup-service"
) {
while (true) {
try {
Thread.sleep(backupInterval)
if (!backupPath.exists()) {
backupPath.mkdirs()
}
val date = formatter.format(Date())
val backupFile = backupPath.resolve("backup_$date.json")
val backup = runBlocking {
backup(*RepositoryType.values())
}
backupFile.writeText(backup)
} catch (e: Exception) {
logger.error(e) { "Cannot create backup" }
}
}
}
}
}
}

View file

@ -10,6 +10,7 @@ sessions = "data/sessions"
uploads = "data/uploads"
database = "data/portal.db"
announcement = "data/announcement.txt"
backup = "data/backup"
[schedule]
reference = "1970-01-01"
@ -27,6 +28,7 @@ sign_key = "d1 20 23 8c 01 f8 f0 0d 9d 7c ff 68 21 97 75 31 38 3f fb 91 20 3a 8d
[general]
allowed_upload_extensions = "png, jpg, jpeg"
wiki_url = ""
backup_interval = 3600000
[twitter]
timeline = ""