diff --git a/backend/.gitignore b/backend/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/backend/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts
new file mode 100644
index 0000000..a96f680
--- /dev/null
+++ b/backend/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ id("kotlin-platform-jvm")
+ id("application")
+ id("com.github.johnrengelman.shadow")
+}
+
+group = "net.underdesk"
+version = "0.0.1"
+
+dependencies {
+ implementation(project(":shared"))
+ implementation(Dependencies.Kotlin.core)
+ implementation(Dependencies.Kotlin.coroutinesCore)
+
+ implementation(Dependencies.Ktor.ktorCore)
+
+ implementation(Dependencies.Firebase.adminSDK)
+}
+
+application {
+ mainClass.set("net.underdesk.circolapp.backend.ServerKt")
+ mainClassName = "net.underdesk.circolapp.backend.ServerKt"
+}
diff --git a/backend/src/main/java/net/underdesk/circolapp/backend/JavaDatabase.kt b/backend/src/main/java/net/underdesk/circolapp/backend/JavaDatabase.kt
new file mode 100644
index 0000000..9c8642f
--- /dev/null
+++ b/backend/src/main/java/net/underdesk/circolapp/backend/JavaDatabase.kt
@@ -0,0 +1,41 @@
+/*
+ * Circolapp
+ * Copyright (C) 2019-2020 Matteo Schiff
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.underdesk.circolapp.backend
+
+import net.underdesk.circolapp.shared.data.AppDatabase
+import net.underdesk.circolapp.shared.data.CircularDao
+import net.underdesk.circolapp.shared.data.DatabaseDriverFactory
+
+object JavaDatabase {
+
+ @Volatile
+ private var instance: AppDatabase? = null
+
+ private fun getInstance(): AppDatabase {
+ return instance ?: synchronized(this) {
+ instance ?: AppDatabase(
+ DatabaseDriverFactory().createDriver()
+ ).also { instance = it }
+ }
+ }
+
+ fun getDaoInstance(): CircularDao {
+ return CircularDao(getInstance())
+ }
+}
diff --git a/backend/src/main/java/net/underdesk/circolapp/backend/PushNotificationUtils.kt b/backend/src/main/java/net/underdesk/circolapp/backend/PushNotificationUtils.kt
new file mode 100644
index 0000000..968d08f
--- /dev/null
+++ b/backend/src/main/java/net/underdesk/circolapp/backend/PushNotificationUtils.kt
@@ -0,0 +1,59 @@
+/*
+ * Circolapp
+ * Copyright (C) 2019-2020 Matteo Schiff
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.underdesk.circolapp.backend
+
+import com.google.firebase.messaging.FirebaseMessaging
+import com.google.firebase.messaging.Message
+import com.google.firebase.messaging.Notification
+import java.time.LocalDateTime
+
+object PushNotificationUtils {
+ fun createPushNotification(topic: String) {
+ val message = Message.builder()
+ .putData("fetchCircular", "true")
+ .setTopic(topic)
+ .build()
+
+ val response = FirebaseMessaging.getInstance().send(message)
+
+ val current = LocalDateTime.now()
+
+ print("Sent data push notification for topic $topic with response $response at time $current \n")
+ }
+
+ fun createPushNotificationiOS(topic: String) {
+ val realTopic = topic + "IOS"
+
+ val message = Message.builder()
+ .setNotification(
+ Notification.builder()
+ .setTitle("Nuove circolari")
+ .setBody("La tua scuola ha pubblicato nuove circolari")
+ .build()
+ )
+ .setTopic(realTopic)
+ .build()
+
+ val response = FirebaseMessaging.getInstance().send(message)
+
+ val current = LocalDateTime.now()
+
+ print("Sent managed push notification for topic $realTopic with response $response at time $current \n")
+ }
+}
diff --git a/backend/src/main/java/net/underdesk/circolapp/backend/Server.kt b/backend/src/main/java/net/underdesk/circolapp/backend/Server.kt
new file mode 100644
index 0000000..660ce28
--- /dev/null
+++ b/backend/src/main/java/net/underdesk/circolapp/backend/Server.kt
@@ -0,0 +1,47 @@
+/*
+ * Circolapp
+ * Copyright (C) 2019-2020 Matteo Schiff
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.underdesk.circolapp.backend
+
+import com.google.auth.oauth2.GoogleCredentials
+import com.google.firebase.FirebaseApp
+import com.google.firebase.FirebaseOptions
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+
+const val CHECK_DELAY_MILLIS = 900000L
+
+fun main(args: Array) {
+ print("Starting Circolapp Push microservice \n")
+ val options = FirebaseOptions.builder()
+ .setCredentials(GoogleCredentials.getApplicationDefault())
+ .build()
+
+ FirebaseApp.initializeApp(options)
+
+ val serverUtils = ServerUtils()
+
+ print("Microservice started! \n")
+ runBlocking {
+ while (true) {
+ serverUtils.checkServers()
+
+ delay(CHECK_DELAY_MILLIS)
+ }
+ }
+}
diff --git a/backend/src/main/java/net/underdesk/circolapp/backend/ServerUtils.kt b/backend/src/main/java/net/underdesk/circolapp/backend/ServerUtils.kt
new file mode 100644
index 0000000..87b2745
--- /dev/null
+++ b/backend/src/main/java/net/underdesk/circolapp/backend/ServerUtils.kt
@@ -0,0 +1,79 @@
+/*
+ * Circolapp
+ * Copyright (C) 2019-2020 Matteo Schiff
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.underdesk.circolapp.backend
+
+import net.underdesk.circolapp.shared.data.Circular
+import net.underdesk.circolapp.shared.server.KtorFactory
+import net.underdesk.circolapp.shared.server.Server
+import net.underdesk.circolapp.shared.server.ServerAPI
+
+class ServerUtils {
+ private val serverList: MutableList = mutableListOf()
+ private val ktorClient = KtorFactory().createClient()
+ private val circularDao = JavaDatabase.getDaoInstance()
+
+ init {
+ for (serverId in ServerAPI.Companion.Servers.values()) {
+ serverList.add(ServerAPI.createServer(serverId, ktorClient))
+ }
+ }
+
+ suspend fun checkServers() {
+ for (server in serverList) {
+ checkServer(server)
+ }
+ }
+
+ private suspend fun updateCirculars(server: Server): Pair, Int> {
+ var onlyNewCirculars = listOf()
+
+ var errorCode = 0
+ val result = server.getCircularsFromServer()
+ if (result.second == ServerAPI.Companion.Result.ERROR)
+ return Pair(emptyList(), -1)
+
+ val oldCirculars = circularDao.getCirculars(server.serverID)
+ val newCirculars = result.first
+
+ if (newCirculars.size != oldCirculars.size) {
+ if (newCirculars.size < oldCirculars.size) {
+ circularDao.deleteAll()
+ errorCode = 1
+ }
+
+ val oldCircularsSize =
+ if (newCirculars.size < oldCirculars.size) 0 else oldCirculars.size
+
+ val circularCount = newCirculars.size - oldCircularsSize
+ onlyNewCirculars = newCirculars.subList(0, circularCount)
+
+ circularDao.insertAll(newCirculars)
+ }
+ return Pair(onlyNewCirculars, errorCode)
+ }
+
+ private suspend fun checkServer(server: Server) {
+ val newCirculars = updateCirculars(server)
+
+ if (newCirculars.second != -1 && newCirculars.first.isNotEmpty()) {
+ PushNotificationUtils.createPushNotification(ServerAPI.getServerTopic(server.serverID))
+ PushNotificationUtils.createPushNotificationiOS(ServerAPI.getServerTopic(server.serverID))
+ }
+ }
+}
diff --git a/build.gradle.kts b/build.gradle.kts
index f717292..66f8121 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -17,6 +17,7 @@ buildscript {
classpath(Config.Plugin.ktlint)
classpath(Config.Plugin.aboutLibraries)
classpath(Config.Plugin.dependencies)
+ classpath(Config.Plugin.shadow)
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt
index 611e78d..14ece82 100644
--- a/buildSrc/src/main/kotlin/Config.kt
+++ b/buildSrc/src/main/kotlin/Config.kt
@@ -13,6 +13,8 @@ object Config {
"com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${Dependencies.AboutLibraries.version}"
const val dependencies =
"com.github.ben-manes:gradle-versions-plugin:0.36.0"
+ const val shadow =
+ "com.github.jengelman.gradle.plugins:shadow:5.2.0"
}
object Android {
diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt
index 6bf6790..4df22ba 100644
--- a/buildSrc/src/main/kotlin/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/Dependencies.kt
@@ -30,6 +30,8 @@ object Dependencies {
object Firebase {
const val bom = "com.google.firebase:firebase-bom:26.1.1"
const val messaging = "com.google.firebase:firebase-messaging-ktx"
+
+ const val adminSDK = "com.google.firebase:firebase-admin:7.0.1"
}
object Ktor {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 019e466..eceaa5a 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,3 +1,4 @@
+include(":backend")
include(":shared")
include(":app")
rootProject.name = "Circolapp"
diff --git a/shared/src/jvmMain/kotlin/net/underdesk/circolapp/shared/server b/shared/src/jvmMain/kotlin/net/underdesk/circolapp/shared/server
new file mode 120000
index 0000000..f1ad6f1
--- /dev/null
+++ b/shared/src/jvmMain/kotlin/net/underdesk/circolapp/shared/server
@@ -0,0 +1 @@
+../../../../../../androidMain/kotlin/net/underdesk/circolapp/shared/server/
\ No newline at end of file