From 6f2037a9dc49de1e498a49296a1adb872e06655f Mon Sep 17 00:00:00 2001
From: Azalea <22280294+hykilpikonna@users.noreply.github.com>
Date: Sun, 26 Oct 2025 12:31:16 +0800
Subject: [PATCH] [+] Upload
---
android/app/src/main/AndroidManifest.xml | 2 +
.../src/main/java/aza/instant/MainActivity.kt | 26 +++++---
.../java/aza/instant/network/BackendClient.kt | 60 +++++++++++++++++++
src/index.ts | 7 ++-
4 files changed, 86 insertions(+), 9 deletions(-)
create mode 100644 android/app/src/main/java/aza/instant/network/BackendClient.kt
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 86fc6e4..980b2bf 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,8 +3,10 @@
xmlns:tools="http://schemas.android.com/tools">
+
(null) }
val scrollState = rememberScrollState()
val coroutineScope = rememberCoroutineScope()
+ val backendClient = remember { BackendClient() }
Column(
modifier = modifier.fillMaxSize(),
@@ -121,16 +124,25 @@ fun FileListerScreen(modifier: Modifier = Modifier) {
picture = renderSvgAndroid(context, svg)
// Save picture to file
- val file = File(latestImage.parent, "${latestImage.nameWithoutExtension}.framed.${System.currentTimeMillis()}.jpg")
- file.createNewFile()
- file.outputStream().use { outputStream ->
+ val renderedImageFile = File(latestImage.parent, "${latestImage.nameWithoutExtension}.framed.${System.currentTimeMillis()}.jpg")
+ renderedImageFile.createNewFile()
+ renderedImageFile.outputStream().use { outputStream ->
Bitmap.createBitmap(picture!!).compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
}
- feedbackText = "Rendered ${latestImage.name}"
+ feedbackText = "Rendered ${latestImage.name}, now uploading..."
+
+ val response = backendClient.uploadImage(
+ id = UUID.randomUUID().toString(),
+ ownerKey = "1234",
+ originalPhoto = latestImage,
+ editedPhoto = renderedImageFile
+ )
+ feedbackText = "Upload response: ${response.status}"
+
} catch (e: Exception) {
- feedbackText = "Error rendering: ${e.message}"
- Log.e("MainActivity", "Error rendering", e)
+ feedbackText = "Error during operation: ${e.message}"
+ Log.e("MainActivity", "Error during render or upload", e)
}
} else {
feedbackText = "No images found in CA_IMAGES"
@@ -138,7 +150,7 @@ fun FileListerScreen(modifier: Modifier = Modifier) {
}
},
) {
- Text(text = "Render Latest Image")
+ Text(text = "Render and Upload")
}
}
diff --git a/android/app/src/main/java/aza/instant/network/BackendClient.kt b/android/app/src/main/java/aza/instant/network/BackendClient.kt
new file mode 100644
index 0000000..bae571a
--- /dev/null
+++ b/android/app/src/main/java/aza/instant/network/BackendClient.kt
@@ -0,0 +1,60 @@
+package aza.instant.network
+
+import io.ktor.client.*
+import io.ktor.client.engine.cio.*
+import io.ktor.client.plugins.HttpTimeoutConfig
+import io.ktor.client.plugins.contentnegotiation.*
+import io.ktor.client.plugins.timeout
+import io.ktor.client.request.*
+import io.ktor.client.request.forms.*
+import io.ktor.client.statement.*
+import io.ktor.http.*
+import io.ktor.serialization.kotlinx.json.*
+import kotlinx.serialization.json.Json
+import java.io.File
+
+class BackendClient {
+
+ private val client = HttpClient(CIO) {
+ install(ContentNegotiation) {
+ json(Json {
+ prettyPrint = true
+ isLenient = true
+ ignoreUnknownKeys = true
+ })
+ }
+ }
+
+ companion object {
+ private const val API_KEY = "meowmeow"
+ private const val SERVER_URL = "http://172.16.0.212:3000" // For Android emulator
+ }
+
+ suspend fun uploadImage(
+ id: String,
+ ownerKey: String,
+ originalPhoto: File,
+ editedPhoto: File
+ ): HttpResponse {
+ val origB = originalPhoto.readBytes()
+ val editB = editedPhoto.readBytes()
+ println(origB.size)
+ println(editB.size)
+ return client.post("$SERVER_URL/upload") {
+ headers {
+ append("x-instant-key", API_KEY)
+ }
+ setBody(
+ MultiPartFormDataContent(
+ formData {
+ append("id", id)
+ append("owner_key", ownerKey)
+ append("\"photo\"", originalPhoto.name, ContentType.Image.JPEG, origB.size.toLong()) { write(origB) }
+ append("\"edited_photo\"", editedPhoto.name, ContentType.Image.JPEG, editB.size.toLong()) { write(editB) }
+ }
+ )
+ )
+ timeout { HttpTimeoutConfig(1 * 60 * 1000L) }
+ }
+ }
+}
diff --git a/src/index.ts b/src/index.ts
index 2a0ecd1..147b272 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -72,7 +72,7 @@ async function getExifData(file: File): Promise {
const INSTANT_KEY = process.env.INSTANT_KEY!
if (!await exists(FOLDER_PHOTOS)) await mkdir(FOLDER_PHOTOS, { recursive: true })
-console.log("Server starting with valid 'secrets.json'.")
+console.log("Server started.")
function checkHeaderKey(headers: Record, expectList: string[] = [INSTANT_KEY]) {
const key = headers["x-instant-key"]
@@ -80,13 +80,16 @@ function checkHeaderKey(headers: Record, expectList:
}
export const app = new Elysia()
+ .get("/", () => "Running!")
+
// Error handling: Return status code and message
.onError(({ error, status }) => {
if (error instanceof HttpError) {
if (error.content instanceof String) return status(error.status, { msg: error.message })
else return status(error.status, error.content)
}
- else return status(500, { error: "Internal Server Error" })
+ // else return status(500, { error: "Internal Server Error" })
+ else throw error
})
.post("/upload", async ({ body, headers, status }) => {