Compare commits
No commits in common. "664f9c17778de062c1c5d128cd9aca2a9c6a66d4" and "f02124cbe80c9764a054cb1f10393a26901d9dc6" have entirely different histories.
664f9c1777
...
f02124cbe8
17 changed files with 35 additions and 138 deletions
|
@ -90,7 +90,6 @@ kotlin {
|
||||||
implementation "io.ktor:ktor-client-apache:$ktor_version"
|
implementation "io.ktor:ktor-client-apache:$ktor_version"
|
||||||
|
|
||||||
implementation 'org.xerial:sqlite-jdbc:3.25.2'
|
implementation 'org.xerial:sqlite-jdbc:3.25.2'
|
||||||
api 'mysql:mysql-connector-java:8.0.16'
|
|
||||||
implementation 'org.jetbrains.exposed:exposed:0.12.2'
|
implementation 'org.jetbrains.exposed:exposed:0.12.2'
|
||||||
|
|
||||||
implementation 'org.mindrot:jbcrypt:0.4'
|
implementation 'org.mindrot:jbcrypt:0.4'
|
||||||
|
|
|
@ -18,9 +18,3 @@ wiki_url = "https://wiki.kif.rocks/w/index.php?title=KIF470:Arbeitskreise&action
|
||||||
|
|
||||||
[twitter]
|
[twitter]
|
||||||
timeline = "https://twitter.com/kiforbiter?ref_src=twsrc%5Etfw"
|
timeline = "https://twitter.com/kiforbiter?ref_src=twsrc%5Etfw"
|
||||||
|
|
||||||
[database]
|
|
||||||
type = "sqlite"
|
|
||||||
url = ""
|
|
||||||
username = ""
|
|
||||||
password = ""
|
|
||||||
|
|
|
@ -69,10 +69,6 @@ fun checkConstraints(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schedule.workGroup.interested > schedule.room.places) {
|
|
||||||
errors += ConstraintError("The work group has more interested then the room has places")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (leader in schedule.workGroup.leader) {
|
for (leader in schedule.workGroup.leader) {
|
||||||
for (s in against) {
|
for (s in against) {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package de.kif.frontend
|
package de.kif.frontend
|
||||||
|
|
||||||
import de.kif.frontend.repository.RoomRepository
|
|
||||||
import de.kif.frontend.repository.ScheduleRepository
|
|
||||||
import de.kif.frontend.repository.WorkGroupRepository
|
|
||||||
import de.kif.frontend.views.board.initBoard
|
import de.kif.frontend.views.board.initBoard
|
||||||
import de.kif.frontend.views.calendar.initCalendar
|
import de.kif.frontend.views.calendar.initCalendar
|
||||||
import de.kif.frontend.views.initAnnouncement
|
import de.kif.frontend.views.initAnnouncement
|
||||||
|
@ -64,35 +61,4 @@ fun main() = init {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = window.location.pathname
|
|
||||||
if ("brett" in url || "wand" in url) {
|
|
||||||
ScheduleRepository.onCreate {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
ScheduleRepository.onUpdate {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
ScheduleRepository.onDelete {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
RoomRepository.onCreate {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
RoomRepository.onUpdate {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
RoomRepository.onDelete {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
WorkGroupRepository.onCreate {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
WorkGroupRepository.onUpdate {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
WorkGroupRepository.onDelete {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,11 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
|
||||||
|
|
||||||
private val htmlBody = document.body ?: createHtmlView()
|
private val htmlBody = document.body ?: createHtmlView()
|
||||||
|
|
||||||
val day = (calendarTable.dataset["day"]?.toIntOrNull() ?: -1)
|
val day = (calendarTable.dataset["day"]?.toIntOrNull() ?: -1).also { println(it) }
|
||||||
val reloadOnFinish = (calendarTable.dataset["reload"]?.toBoolean() ?: false)
|
val reloadOnFinish = (calendarTable.dataset["reload"]?.toBoolean() ?: false).also { println(it) }
|
||||||
val hideEmpty = (calendarTable.dataset["hide-empty"]?.toBoolean() ?: false)
|
val referenceDate = (calendarTable.dataset["reference"]?.toLongOrNull() ?: -1L).also { println(it) }
|
||||||
val referenceDate = (calendarTable.dataset["reference"]?.toLongOrNull() ?: -1L)
|
val nowDate = (calendarTable.dataset["now"]?.toLongOrNull() ?: -1L).also { println(it) }
|
||||||
val nowDate = (calendarTable.dataset["now"]?.toLongOrNull() ?: -1L)
|
val timeDifference = (Date.now().toLong() - nowDate).also { println(it) }
|
||||||
val timeDifference = (Date.now().toLong() - nowDate)
|
|
||||||
|
|
||||||
fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
|
fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
|
||||||
scrollAllVerticalBy(pixel, scrollBehavior)
|
scrollAllVerticalBy(pixel, scrollBehavior)
|
||||||
|
|
|
@ -74,12 +74,18 @@ class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection<C
|
||||||
min = (min / 60 - 1) * 60
|
min = (min / 60 - 1) * 60
|
||||||
max = (max / 60 + 2) * 60
|
max = (max / 60 + 2) * 60
|
||||||
|
|
||||||
|
if (min == minTime && max == maxTime) return
|
||||||
|
|
||||||
minTime = min
|
minTime = min
|
||||||
maxTime = max
|
maxTime = max
|
||||||
|
|
||||||
min = calendarBodies.map { it.minTime }.min() ?: min
|
min = calendarBodies.map { it.minTime }.min() ?: min
|
||||||
max = calendarBodies.map { it.maxTime }.max() ?: max
|
max = calendarBodies.map { it.maxTime }.max() ?: max
|
||||||
|
|
||||||
|
calendarBodies.filter { it != this }.forEach {
|
||||||
|
it.updateRows()
|
||||||
|
}
|
||||||
|
|
||||||
while (isNotEmpty() && min > first().time) {
|
while (isNotEmpty() && min > first().time) {
|
||||||
remove(first())
|
remove(first())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import org.w3c.dom.HTMLElement
|
||||||
import org.w3c.dom.HTMLSpanElement
|
import org.w3c.dom.HTMLSpanElement
|
||||||
import org.w3c.dom.set
|
import org.w3c.dom.set
|
||||||
|
|
||||||
class CalendarRow(val calendar: CalendarBody, view: HTMLElement) : ViewCollection<CalendarCell>(view) {
|
class CalendarRow(calendar: CalendarBody, view: HTMLElement) : ViewCollection<CalendarCell>(view) {
|
||||||
val day = calendar.day
|
val day = calendar.day
|
||||||
|
|
||||||
val time = dataset["time"]?.toIntOrNull() ?: 0
|
val time = dataset["time"]?.toIntOrNull() ?: 0
|
||||||
|
@ -49,6 +49,8 @@ class CalendarRow(val calendar: CalendarBody, view: HTMLElement) : ViewCollectio
|
||||||
}
|
}
|
||||||
row.html.appendChild(rowHeader)
|
row.html.appendChild(rowHeader)
|
||||||
|
|
||||||
|
row.html
|
||||||
|
|
||||||
val rooms = RoomRepository.all()
|
val rooms = RoomRepository.all()
|
||||||
|
|
||||||
for (room in rooms) {
|
for (room in rooms) {
|
||||||
|
|
|
@ -169,10 +169,6 @@
|
||||||
background-color: var(--table-header-color) !important;
|
background-color: var(--table-header-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-cell[data-empty = "true"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-tools {
|
.calendar-tools {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5rem;
|
top: -5rem;
|
||||||
|
|
|
@ -133,20 +133,6 @@ object Configuration {
|
||||||
val time by c(ResoSpec.time)
|
val time by c(ResoSpec.time)
|
||||||
}
|
}
|
||||||
|
|
||||||
private object DatabaseSpec : ConfigSpec("database") {
|
|
||||||
val type by required<String>()
|
|
||||||
val url by required<String>()
|
|
||||||
val username by required<String>()
|
|
||||||
val password by required<String>()
|
|
||||||
}
|
|
||||||
|
|
||||||
object Database {
|
|
||||||
val type by c(DatabaseSpec.type)
|
|
||||||
val url by c(DatabaseSpec.url)
|
|
||||||
val username by c(DatabaseSpec.username)
|
|
||||||
val password by c(DatabaseSpec.password)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
var config = Config {
|
var config = Config {
|
||||||
addSpec(ServerSpec)
|
addSpec(ServerSpec)
|
||||||
|
@ -156,7 +142,6 @@ object Configuration {
|
||||||
addSpec(GeneralSpec)
|
addSpec(GeneralSpec)
|
||||||
addSpec(TwitterSpec)
|
addSpec(TwitterSpec)
|
||||||
addSpec(ResoSpec)
|
addSpec(ResoSpec)
|
||||||
addSpec(DatabaseSpec)
|
|
||||||
}.from.toml.resource("portal.toml")
|
}.from.toml.resource("portal.toml")
|
||||||
|
|
||||||
for (file in Files.list(Paths.get("."))) {
|
for (file in Files.list(Paths.get("."))) {
|
||||||
|
|
|
@ -26,39 +26,14 @@ object Connection {
|
||||||
)
|
)
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
val type = Configuration.Database.type
|
val dbPath = Configuration.Path.databasePath.toString()
|
||||||
val url = Configuration.Database.url
|
Database.connect("jdbc:sqlite:$dbPath", "org.sqlite.JDBC")
|
||||||
val username = Configuration.Database.username
|
|
||||||
val password = Configuration.Database.password
|
|
||||||
when (type) {
|
|
||||||
"mysql" -> {
|
|
||||||
Database.connect(
|
|
||||||
"jdbc:mysql://$url:3306/akplan?user=$username&password=$password&serverTimezone=UTC",
|
|
||||||
"com.mysql.cj.jdbc.Driver",
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"mariadb" -> {
|
|
||||||
Database.connect(
|
|
||||||
"jdbc:mariadb://$url:3306/akplan?user=$username&password=$password&serverTimezone=UTC",
|
|
||||||
"org.mariadb.jdbc.Driver",
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val dbPath = Configuration.Path.databasePath.toString()
|
|
||||||
Database.connect("jdbc:sqlite:$dbPath", "org.sqlite.JDBC")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionManager.manager.defaultIsolationLevel = TRANSACTION_SERIALIZABLE
|
TransactionManager.manager.defaultIsolationLevel = TRANSACTION_SERIALIZABLE
|
||||||
|
|
||||||
try {
|
try {
|
||||||
create()
|
create()
|
||||||
} catch (e: ExposedSQLException) {
|
} catch (e: ExposedSQLException) {
|
||||||
logger.error(e) { "Cannot initialize the database!" }
|
logger.error { "Cannot initialize the database!" }
|
||||||
|
|
||||||
print("Do you want to recreate the database (you will lose all data)? [y/N]: ")
|
print("Do you want to recreate the database (you will lose all data)? [y/N]: ")
|
||||||
val result = readLine()
|
val result = readLine()
|
||||||
|
|
|
@ -29,7 +29,7 @@ object DbWorkGroup : Table() {
|
||||||
val accessible = bool("accessible")
|
val accessible = bool("accessible")
|
||||||
|
|
||||||
val language = enumeration("language", Language::class)
|
val language = enumeration("language", Language::class)
|
||||||
val leader = text("leader")
|
val leader = text("leader").default("[]")
|
||||||
|
|
||||||
val length = integer("length")
|
val length = integer("length")
|
||||||
val constraints = text("constraints")
|
val constraints = text("constraints")
|
||||||
|
@ -49,9 +49,9 @@ object DbRoom : Table() {
|
||||||
val whiteboard = bool("whiteboard")
|
val whiteboard = bool("whiteboard")
|
||||||
val blackboard = bool("blackboard")
|
val blackboard = bool("blackboard")
|
||||||
val accessible = bool("accessible")
|
val accessible = bool("accessible")
|
||||||
val pool = bool("pool")
|
val pool = bool("pool").default(false)
|
||||||
|
|
||||||
val blocked = text("blocked")
|
val blocked = text("blocked").default("[]")
|
||||||
|
|
||||||
val createdAt = long("createdAt")
|
val createdAt = long("createdAt")
|
||||||
val updatedAt = long("updatedAt")
|
val updatedAt = long("updatedAt")
|
||||||
|
@ -63,8 +63,8 @@ object DbSchedule : Table() {
|
||||||
val roomId = long("room_id").index()
|
val roomId = long("room_id").index()
|
||||||
val day = integer("day").index()
|
val day = integer("day").index()
|
||||||
val time = integer("time_slot")
|
val time = integer("time_slot")
|
||||||
val lockRoom = bool("lock_room")
|
val lockRoom = bool("lock_room").default(false)
|
||||||
val lockTime = bool("lock_time")
|
val lockTime = bool("lock_time").default(false)
|
||||||
|
|
||||||
val createdAt = long("createdAt")
|
val createdAt = long("createdAt")
|
||||||
val updatedAt = long("updatedAt")
|
val updatedAt = long("updatedAt")
|
||||||
|
|
|
@ -6,12 +6,10 @@ import de.kif.backend.prefix
|
||||||
import de.kif.backend.repository.*
|
import de.kif.backend.repository.*
|
||||||
import de.kif.backend.route.api.error
|
import de.kif.backend.route.api.error
|
||||||
import de.kif.backend.util.Backup
|
import de.kif.backend.util.Backup
|
||||||
import de.kif.backend.util.PushService
|
|
||||||
import de.kif.backend.util.WikiImporter
|
import de.kif.backend.util.WikiImporter
|
||||||
import de.kif.backend.view.respondMain
|
import de.kif.backend.view.respondMain
|
||||||
import de.kif.common.RepositoryType
|
import de.kif.common.RepositoryType
|
||||||
import de.kif.common.model.Permission
|
import de.kif.common.model.Permission
|
||||||
import de.kif.common.model.Post
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
|
@ -53,10 +51,6 @@ fun Route.account() {
|
||||||
+"Sicherung"
|
+"Sicherung"
|
||||||
}
|
}
|
||||||
|
|
||||||
a(href = "$prefix/account/reload", classes = "form-btn") {
|
|
||||||
+"Alle Geräte neu laden"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.checkPermission(Permission.SCHEDULE)) {
|
if (user.checkPermission(Permission.SCHEDULE)) {
|
||||||
a(href = "$prefix/account/import", classes = "form-btn") {
|
a(href = "$prefix/account/import", classes = "form-btn") {
|
||||||
+"Aus Wiki importieren"
|
+"Aus Wiki importieren"
|
||||||
|
@ -522,8 +516,4 @@ fun Route.account() {
|
||||||
call.error(HttpStatusCode.Unauthorized)
|
call.error(HttpStatusCode.Unauthorized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get("/account/reload") {
|
|
||||||
PushService.signature = Post.generateUrl()
|
|
||||||
call.respondRedirect("$prefix/account")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,8 +175,7 @@ fun Route.board() {
|
||||||
max,
|
max,
|
||||||
rooms,
|
rooms,
|
||||||
schedules,
|
schedules,
|
||||||
reloadAfterFinish = true,
|
reloadAfterFinish = true
|
||||||
hideEmptyRooms = true
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
div("board-twitter") {
|
div("board-twitter") {
|
||||||
|
|
|
@ -72,8 +72,7 @@ fun DIV.renderCalendar(
|
||||||
to: Int,
|
to: Int,
|
||||||
rooms: List<Room>,
|
rooms: List<Room>,
|
||||||
schedules: Map<Room, Map<Int, List<Schedule>>>,
|
schedules: Map<Room, Map<Int, List<Schedule>>>,
|
||||||
reloadAfterFinish: Boolean = false,
|
reloadAfterFinish: Boolean = false
|
||||||
hideEmptyRooms: Boolean = false
|
|
||||||
) {
|
) {
|
||||||
val gridLabelWidth = 60
|
val gridLabelWidth = 60
|
||||||
val minutesOfDay = to - from
|
val minutesOfDay = to - from
|
||||||
|
@ -92,7 +91,6 @@ fun DIV.renderCalendar(
|
||||||
attributes["data-reference"] = Configuration.Schedule.referenceDate.time.toString()
|
attributes["data-reference"] = Configuration.Schedule.referenceDate.time.toString()
|
||||||
attributes["data-now"] = now.timeInMillis.toString()
|
attributes["data-now"] = now.timeInMillis.toString()
|
||||||
attributes["data-reload"] = reloadAfterFinish.toString()
|
attributes["data-reload"] = reloadAfterFinish.toString()
|
||||||
attributes["data-hide-empty"] = hideEmptyRooms.toString()
|
|
||||||
|
|
||||||
div("calendar-table-box ${orientation.name.toLowerCase().replace("_", "-")}") {
|
div("calendar-table-box ${orientation.name.toLowerCase().replace("_", "-")}") {
|
||||||
div("calendar-header") {
|
div("calendar-header") {
|
||||||
|
@ -105,7 +103,6 @@ fun DIV.renderCalendar(
|
||||||
for (room in rooms) {
|
for (room in rooms) {
|
||||||
div("calendar-cell") {
|
div("calendar-cell") {
|
||||||
attributes["data-room"] = room.id.toString()
|
attributes["data-room"] = room.id.toString()
|
||||||
attributes["data-empty"] = (hideEmptyRooms && room !in schedules).toString()
|
|
||||||
|
|
||||||
span {
|
span {
|
||||||
+room.name
|
+room.name
|
||||||
|
@ -150,7 +147,6 @@ fun DIV.renderCalendar(
|
||||||
div("calendar-cell") {
|
div("calendar-cell") {
|
||||||
attributes["data-room"] = room.id.toString()
|
attributes["data-room"] = room.id.toString()
|
||||||
attributes["data-blocked"] = blocked.toString()
|
attributes["data-blocked"] = blocked.toString()
|
||||||
attributes["data-empty"] = (hideEmptyRooms && room !in schedules).toString()
|
|
||||||
|
|
||||||
title = room.name + " - " + timeString
|
title = room.name + " - " + timeString
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package de.kif.backend.route
|
package de.kif.backend.route
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import com.soywiz.klock.locale.german
|
||||||
import de.kif.backend.Configuration
|
import de.kif.backend.Configuration
|
||||||
import de.kif.backend.repository.RoomRepository
|
import de.kif.backend.repository.RoomRepository
|
||||||
import de.kif.backend.repository.ScheduleRepository
|
import de.kif.backend.repository.ScheduleRepository
|
||||||
import de.kif.backend.view.respondMain
|
import de.kif.backend.view.respondMain
|
||||||
import de.kif.common.formatDate
|
|
||||||
import de.kif.common.formatDateWithoutYear
|
|
||||||
import de.kif.common.model.Room
|
import de.kif.common.model.Room
|
||||||
import de.kif.common.model.Schedule
|
import de.kif.common.model.Schedule
|
||||||
import io.ktor.routing.Route
|
import io.ktor.routing.Route
|
||||||
|
@ -63,8 +63,6 @@ fun Route.wall() {
|
||||||
wallStart + 2
|
wallStart + 2
|
||||||
).map { genWallData(it) }
|
).map { genWallData(it) }
|
||||||
|
|
||||||
val rooms = RoomRepository.all()
|
|
||||||
|
|
||||||
var min = days.mapNotNull { it.min }.min() ?: 12 * 60
|
var min = days.mapNotNull { it.min }.min() ?: 12 * 60
|
||||||
val max = days.mapNotNull { it.max }.max() ?: 12 * 60
|
val max = days.mapNotNull { it.max }.max() ?: 12 * 60
|
||||||
|
|
||||||
|
@ -72,15 +70,18 @@ fun Route.wall() {
|
||||||
min = max
|
min = max
|
||||||
}
|
}
|
||||||
|
|
||||||
val refDate = Configuration.Schedule.referenceDate.time
|
val refDate = DateTime(Configuration.Schedule.referenceDate.time)
|
||||||
|
|
||||||
respondMain(true, true) {
|
respondMain(true, true) {
|
||||||
content {
|
content {
|
||||||
div("wall") {
|
div("wall") {
|
||||||
for (day in days) {
|
for (day in days) {
|
||||||
|
|
||||||
val date = refDate + (day.number * 1000 * 60 * 60 * 24)
|
val date = refDate + day.number.days
|
||||||
val dateString = formatDateWithoutYear(date, Configuration.Schedule.offset)
|
val dateString = DateFormat("EEEE, d. MMMM")
|
||||||
|
.withLocale(KlockLocale.german)
|
||||||
|
.format(date)
|
||||||
|
|
||||||
|
|
||||||
div("wall-box") {
|
div("wall-box") {
|
||||||
div("wall-name") {
|
div("wall-name") {
|
||||||
|
@ -94,9 +95,8 @@ fun Route.wall() {
|
||||||
day.number,
|
day.number,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
rooms,
|
day.schedules.keys.toList().sortedBy { it.id },
|
||||||
day.schedules,
|
day.schedules
|
||||||
hideEmptyRooms = true
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import kotlin.concurrent.thread
|
||||||
object PushService {
|
object PushService {
|
||||||
|
|
||||||
internal var leastValidTimestamp = System.currentTimeMillis()
|
internal var leastValidTimestamp = System.currentTimeMillis()
|
||||||
var signature = Post.generateUrl()
|
val signature = Post.generateUrl()
|
||||||
|
|
||||||
private val messages: MutableList<Pair<Long, Message>> = mutableListOf()
|
private val messages: MutableList<Pair<Long, Message>> = mutableListOf()
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,3 @@ backup_interval = 3600000
|
||||||
|
|
||||||
[twitter]
|
[twitter]
|
||||||
timeline = ""
|
timeline = ""
|
||||||
|
|
||||||
[database]
|
|
||||||
type = "sqlite"
|
|
||||||
url = ""
|
|
||||||
username = ""
|
|
||||||
password = ""
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue