package com.dano.test1 import android.content.Context import android.database.sqlite.SQLiteDatabase import android.util.Base64 import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import android.database.Cursor import okhttp3.* import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.asRequestBody import org.json.JSONArray import org.json.JSONObject import java.io.File import java.io.IOException import kotlin.system.exitProcess object DatabaseUploader { private const val DB_NAME = "questionnaire_database" private const val SERVER_FULL_URL = "http://49.13.157.44/uploadFull.php" private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDelta.php" private const val SERVER_CHECK_URL = "http://49.13.157.44/checkDatabaseExists.php" private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345" private val client = OkHttpClient() fun uploadDatabase(context: Context) { CoroutineScope(Dispatchers.IO).launch { try { val dbFile = context.getDatabasePath(DB_NAME) if (!dbFile.exists()) { Log.e("UPLOAD", "Datenbankdatei existiert nicht: ${dbFile.absolutePath}") return@launch } // WAL-Checkpoint try { val db = SQLiteDatabase.openDatabase( dbFile.absolutePath, null, SQLiteDatabase.OPEN_READWRITE ) db.rawQuery("PRAGMA wal_checkpoint(FULL);", null).use { cursor -> if (cursor.moveToFirst()) { try { Log.d("UPLOAD", "WAL-Checkpoint result: ${cursor.getInt(0)}") } catch (_: Exception) {} } } db.close() Log.d("UPLOAD", "WAL-Checkpoint erfolgreich.") } catch (e: Exception) { Log.e("UPLOAD", "Fehler beim WAL-Checkpoint", e) } val exists = checkDatabaseExists() if (exists) { Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload") uploadDelta(dbFile) } else { Log.d("UPLOAD", "Keine Server-Datenbank → Full-Upload") uploadFull(dbFile) } } catch (e: Exception) { Log.e("UPLOAD", "Fehler beim Hochladen der DB", e) } } } private fun checkDatabaseExists(): Boolean { return try { val request = Request.Builder() .url(SERVER_CHECK_URL) .get() .build() client.newCall(request).execute().use { response -> if (!response.isSuccessful) { Log.e("UPLOAD", "checkDatabaseExists HTTP error: ${response.code}") return false } val body = response.body?.string() ?: return false try { val j = JSONObject(body) j.optBoolean("exists", false) } catch (e: Exception) { body.contains("exists", ignoreCase = true) } } } catch (e: Exception) { Log.e("UPLOAD", "Fehler bei Server-Prüfung", e) false } } private fun uploadFull(file: File) { val requestBody = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart( "file", file.name, file.asRequestBody("application/octet-stream".toMediaTypeOrNull()) ) .addFormDataPart("token", API_TOKEN) .build() val request = Request.Builder() .url(SERVER_FULL_URL) .post(requestBody) .build() client.newCall(request).enqueue(serverCallback(file)) } private fun uploadDelta(file: File) { try { val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY) val data = JSONObject().apply { put("clients", queryToJsonArray(db, "SELECT clientCode FROM clients")) put("questionnaires", queryToJsonArray(db, "SELECT id FROM questionnaires")) put("questions", queryToJsonArray(db, "SELECT questionId, questionnaireId, question FROM questions")) put("answers", queryToJsonArray(db, "SELECT clientCode, questionId, answerValue FROM answers")) put("completed_questionnaires", queryToJsonArray(db, "SELECT clientCode, questionnaireId, timestamp, isDone, sumPoints FROM completed_questionnaires") ) } db.close() val requestBody = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("token", API_TOKEN) .addFormDataPart("data", data.toString()) .build() val request = Request.Builder() .url(SERVER_DELTA_URL) .post(requestBody) .build() client.newCall(request).enqueue(serverCallback(file)) } catch (e: Exception) { Log.e("UPLOAD", "Fehler beim Delta-Upload", e) } } private fun queryToJsonArray(db: SQLiteDatabase, query: String): JSONArray { val cursor = db.rawQuery(query, null) val jsonArray = JSONArray() cursor.use { val columnNames = it.columnNames while (it.moveToNext()) { val obj = JSONObject() for (col in columnNames) { val idx = it.getColumnIndex(col) if (idx >= 0) { when (it.getType(idx)) { Cursor.FIELD_TYPE_INTEGER -> obj.put(col, it.getLong(idx)) Cursor.FIELD_TYPE_FLOAT -> obj.put(col, it.getDouble(idx)) Cursor.FIELD_TYPE_STRING -> obj.put(col, it.getString(idx)) Cursor.FIELD_TYPE_NULL -> obj.put(col, JSONObject.NULL) Cursor.FIELD_TYPE_BLOB -> { val blob = it.getBlob(idx) obj.put(col, Base64.encodeToString(blob, Base64.NO_WRAP)) } else -> obj.put(col, it.getString(idx)) } } } jsonArray.put(obj) } } return jsonArray } private fun serverCallback(file: File) = object : Callback { override fun onFailure(call: Call, e: IOException) { Log.e("UPLOAD", "Upload fehlgeschlagen: ${e.message}") } override fun onResponse(call: Call, response: Response) { val body = try { response.body?.string() ?: "Keine Response" } catch (e: Exception) { "Fehler beim Lesen der Response: ${e.message}" } if (response.isSuccessful) { Log.d("UPLOAD", "Upload erfolgreich: $body") if (file.delete()) { Log.d("UPLOAD", "Lokale DB gelöscht.") exitProcess(0) } else { Log.e("UPLOAD", "Löschen der lokalen DB fehlgeschlagen.") } } else { Log.e("UPLOAD", "Upload fehlgeschlagen: ${response.code} $body") } } } }