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 }) => {