wear backend/common stub

This commit is contained in:
2026-09-08 23:05:05 +03:00
parent 410c879d0d
commit 8ba9ce6bd4
13 changed files with 268 additions and 1 deletions
+4
View File
@@ -0,0 +1,4 @@
/build
/videobox
/bin
back.env
+101
View File
@@ -0,0 +1,101 @@
plugins {
alias(libs.plugins.convention.backend.app)
alias(libs.plugins.ktor)
alias(libs.plugins.sqldelight)
alias(libs.plugins.serialization)
alias(libs.plugins.convention.koin)
}
sqldelight {
databases {
create("AppDatabase") {
packageName.set("ru.shadowsparky.gigawear.backend")
dialect("app.cash.sqldelight:postgresql-dialect:2.0.2")
}
}
}
dependencies {
implementation(project(":apps:giga-wear:common"))
implementation(project(":libs:backend-base"))
implementation(project(":libs:base"))
implementation(project(":libs:http-client"))
implementation(project(":feature:updater:updater-common"))
implementation(project(":feature:chat:chat-common"))
implementation(project(":feature:chat:chat-backend"))
implementation(libs.ktor.server.core.jvm)
implementation(libs.ktor.server.host.common.jvm)
implementation(libs.ktor.server.status.pages.jvm)
implementation(libs.ktor.server.content.negotiation.jvm)
implementation(libs.ktor.serialization.kotlinx.json.jvm)
implementation(libs.ktor.server.netty.jvm)
implementation(libs.ktor.server.auth)
implementation(libs.ktor.server.auth.jwt)
implementation(libs.ktor.server.websockets.jvm)
implementation(libs.ktor.server.cors)
implementation(libs.androidx.datastore.preferences.core)
implementation(libs.jdbc.driver)
implementation(libs.postgresql)
implementation(libs.coroutines.extensions)
}
java {
sourceCompatibility = JavaVersion.toVersion(libs.versions.proj.server.java.get())
targetCompatibility = JavaVersion.toVersion(libs.versions.proj.server.java.get())
}
group = "ru.shadowsparky.gigawear"
version = "1.0"
val mainClassName = "ru.shadowsparky.gigawear.backend.MainKt"
application {
mainClass.set(mainClassName)
val isDevelopment: Boolean = project.ext.has("development")
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
}
ktor {
fatJar {
archiveFileName.set("server-fat.jar")
}
}
tasks {
named("build") {
dependsOn("buildFatJar")
}
register<Exec>("deployRemote") {
dependsOn("buildFatJar")
commandLine(
getCommandLine(
"docker context use remote",
"docker build . -t gigawear-server",
"docker context use default"
)
)
notCompatibleWithConfigurationCache("This task uses Exec which is not compatible with configuration cache")
}
register<Exec>("deployLocal") {
dependsOn("buildFatJar")
commandLine(
getCommandLine(
"docker compose down",
"docker compose up -d --build --force-recreate"
)
)
notCompatibleWithConfigurationCache("This task uses Exec which is not compatible with configuration cache")
}
}
private fun getCommandLine(vararg commands: String): List<String> {
val osName = System.getProperty("os.name").lowercase()
return if (osName.contains("win")) {
listOf("cmd", "/c") + commands.joinToString(" && ")
} else {
listOf("sh", "-c") + commands.joinToString(" && ")
}
}
@@ -0,0 +1,23 @@
package ru.shadowsparky.gigawear.backend
import org.koin.core.annotation.ComponentScan
import org.koin.core.annotation.Configuration
import org.koin.core.annotation.Module
import org.koin.core.annotation.Single
import ru.shadowsparky.backend.data.EnvFetcher
import ru.shadowsparky.backend.domain.JwtInfo
@Module
@ComponentScan
@Configuration
class BackendModule {
@Single
fun provideJwtInfo(envFetcher: EnvFetcher): JwtInfo {
return JwtInfo(
envFetcher.get("GIGA_WEAR_SECRET", "secret"),
"https://shadowsparky.ru/",
"https://wear.shadowsparky.ru/",
"wear"
)
}
}
@@ -0,0 +1,27 @@
package ru.shadowsparky.gigawear.backend
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import org.koin.core.annotation.KoinApplication
import org.koin.core.component.KoinComponent
import org.koin.plugin.module.dsl.startKoin
@KoinApplication
object GigaWearApp
fun main(args: Array<String>) {
startKoin<GigaWearApp>()
io.ktor.server.netty.EngineMain.main(args)
}
fun Application.module() {
val koin = object : KoinComponent {}
this.routing {
get("health") {
this.call.respond(HttpStatusCode.OK)
}
}
}
@@ -0,0 +1,11 @@
package ru.shadowsparky.gigawear.backend.data
import org.koin.core.annotation.Factory
import ru.shadowsparky.chat.backend.domain.ChatService
@Factory
class ClientIdProviderImpl : ChatService.ClientIdProvider {
override suspend fun provide(userId: Long): String {
return "gigawear-$userId"
}
}
@@ -0,0 +1,10 @@
package ru.shadowsparky.gigawear.backend.data
import org.koin.core.annotation.Factory
import ru.shadowsparky.backend.domain.LoginVerifier
@Factory
class LoginVerifierImpl : LoginVerifier {
override suspend fun verify(login: String) {
}
}
@@ -0,0 +1,48 @@
package ru.shadowsparky.gigawear.backend.data
import org.koin.core.annotation.Single
import ru.shadowsparky.chat.backend.domain.MessageStorage
import ru.shadowsparky.chat.domain.Message
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
@Single
class MessageStorageImpl : MessageStorage {
private val storage = ConcurrentHashMap<Long, MutableList<Message>>()
private val idGenerator = AtomicLong(1)
override suspend fun query(
userId: Long,
afterId: Long?,
limit: Int
): List<Message> {
val userMessages = storage[userId] ?: return emptyList()
synchronized(userMessages) {
val filteredList = if (afterId != null) {
val index = userMessages.indexOfFirst { it.id == afterId }
if (index != -1 && index + 1 < userMessages.size) {
userMessages.subList(index + 1, userMessages.size)
} else {
emptyList()
}
} else {
userMessages
}
return filteredList.take(limit).toList()
}
}
override suspend fun insert(
userId: Long,
message: Message
): Long {
val userMessages = storage.computeIfAbsent(userId) { ArrayList() }
val newId = idGenerator.getAndIncrement()
val messageToInsert = message.copy(id = newId)
synchronized(userMessages) {
userMessages.add(messageToInsert)
}
return newId
}
}
@@ -0,0 +1,8 @@
ktor {
deployment {
port = 8181
}
application {
modules = [ ru.shadowsparky.gigawear.backend.MainKt.module ]
}
}
@@ -0,0 +1,12 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.eclipse.jetty" level="INFO" />
<logger name="io.netty" level="INFO" />
</configuration>
+1
View File
@@ -25,6 +25,7 @@ android {
dependencies { dependencies {
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
implementation(project(":apps:giga-wear:common"))
implementation(project(":libs:ui")) implementation(project(":libs:ui"))
implementation(project(":libs:http-client")) implementation(project(":libs:http-client"))
implementation(project(":libs:base")) implementation(project(":libs:base"))
+1
View File
@@ -0,0 +1 @@
build
+20
View File
@@ -0,0 +1,20 @@
plugins {
alias(libs.plugins.convention.kmp)
alias(libs.plugins.convention.android.lib)
alias(libs.plugins.convention.ktor.client)
alias(libs.plugins.convention.compose)
alias(libs.plugins.convention.serialization)
alias(libs.plugins.convention.koin)
}
kotlin {
sourceSets {
commonMain.dependencies {
implementation(project(":libs:base"))
implementation(project(":libs:http-client"))
}
}
android {
namespace = "ru.shadowsparky.gigawear.common"
}
}
+2 -1
View File
@@ -38,8 +38,9 @@ include(":apps:experimentarium:androidApp")
include(":apps:experimentarium:shared") include(":apps:experimentarium:shared")
include(":apps:experimentarium:web") include(":apps:experimentarium:web")
include(":apps:giga-wear")
include(":apps:giga-wear:client") include(":apps:giga-wear:client")
include(":apps:giga-wear:common")
include(":apps:giga-wear:backend")
include(":libs:backend-base") include(":libs:backend-base")
include(":libs:base") include(":libs:base")