full reborn
This commit is contained in:
.github/workflows
LICENSEREADME.mdbuild.gradlebusiness_cases/post_creating
client
common
src
commonMain
kotlin
dev
inmo
postssystem
business_cases
post_creating
main
server
client
build.gradle
src
commonMain
kotlin
dev
inmo
jsMain
kotlin
dev
resources
jvmMain
kotlin
dev
inmo
postssystem
client
main
core
api
src
commonMain
kotlin
dev
inmo
postssystem
core
commonTest
kotlin
dev
inmo
postssystem
core
jvmMain
kotlin
dev
inmo
postssystem
core
content
api
business
content_adapters
binary
main
exposed
build.gradlegradle.properties
src
jvmMain
kotlin
dev
inmo
postssystem
core
exposed
jvmTest
kotlin
dev
inmo
postssystem
ktor
client
common
src
commonMain
kotlin
dev
inmo
postssystem
main
server
features
auth
client
common
server
common
client
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
common
jvmMain
kotlin
dev
inmo
postssystem
features
common
common
main
server
files
client
build.gradle
src
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
jvmMain
kotlin
dev
inmo
postssystem
features
main
server
roles
client
common
manager
client
common
server
server
status
template
client
common
server
users
client
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
users
jvmMain
kotlin
dev
inmo
postssystem
features
users
common
main
server
gradle/wrapper
gradlewgradlew.batmimes_generator
mppAndroidProject.gradlemppJavaProject.gradlemppJsProject.gradlemppProjectWithSerialization.gradlepubconf.kpsbpublish.gradlepublish.kpsbpublishing
api
src
commonMain
kotlin
main
exposed
ktor
client
src
commonMain
kotlin
com
insanusmokrassar
postssystem
main
common
src
commonMain
kotlin
com
insanusmokrassar
main
server
server
settings.gradle
21
server/Makefile
Normal file
21
server/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
#!make
|
||||
# include .env
|
||||
export $(shell sed 's/=.*//' .env)
|
||||
|
||||
.ONESHELL:
|
||||
.PHONY: clean build upPostgres composeBuild runExample addTestUserAndAuth startTestPostgres
|
||||
|
||||
clean:
|
||||
../gradlew clean
|
||||
|
||||
build:
|
||||
../gradlew build distTar
|
||||
|
||||
startTestServer:
|
||||
../gradlew run --args="test.config.json"
|
||||
|
||||
startTestPostgres:
|
||||
sudo docker-compose up
|
||||
|
||||
addTestUserAndAuth:
|
||||
docker-compose exec test_postgres psql test -U test -c "INSERT INTO Users VALUES (-1, 'test', 'test', 'test');INSERT INTO UsersAuthentications VALUES ('test', -1);"
|
25
server/build.gradle
Normal file
25
server/build.gradle
Normal file
@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "application"
|
||||
}
|
||||
|
||||
version = null
|
||||
|
||||
application {
|
||||
mainClass = 'dev.inmo.postssystem.server.EntranceKt'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(":postssystem.features.common.server")
|
||||
api project(":postssystem.features.status.server")
|
||||
api project(":postssystem.features.files.server")
|
||||
api project(":postssystem.features.users.server")
|
||||
api project(":postssystem.features.auth.server")
|
||||
api project(":postssystem.features.roles.server")
|
||||
api project(":postssystem.features.roles.manager.server")
|
||||
api "io.ktor:ktor-server-netty:$ktor_version"
|
||||
api "io.ktor:ktor-websockets:$ktor_version"
|
||||
api "org.jetbrains.exposed:exposed-jdbc:$kotlin_exposed_version"
|
||||
api "org.postgresql:postgresql:$psql_version"
|
||||
}
|
11
server/docker-compose.yml
Normal file
11
server/docker-compose.yml
Normal file
@ -0,0 +1,11 @@
|
||||
version: "3.4"
|
||||
|
||||
services:
|
||||
test_postgres:
|
||||
image: postgres
|
||||
environment:
|
||||
POSTGRES_USER: "test"
|
||||
POSTGRES_PASSWORD: "test"
|
||||
POSTGRES_DB: "test"
|
||||
ports:
|
||||
- "8090:5432"
|
@ -0,0 +1,10 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import dev.inmo.postssystem.features.common.common.Milliseconds
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Serializable
|
||||
data class AuthConfig(
|
||||
val sessionAge: Milliseconds = TimeUnit.DAYS.toMillis(1)
|
||||
)
|
@ -0,0 +1,29 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.content.files
|
||||
import io.ktor.http.content.static
|
||||
import io.ktor.response.respondRedirect
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.get
|
||||
import java.io.File
|
||||
|
||||
class ClientStaticRoutingConfiguration(
|
||||
clientStatic: String?
|
||||
) : ApplicationRoutingConfigurator.Element {
|
||||
private val staticFile = clientStatic ?.let { File(clientStatic).takeIf { it.exists() } }
|
||||
override fun Route.invoke() {
|
||||
staticFile ?.let {
|
||||
static("client") {
|
||||
files(it)
|
||||
get {
|
||||
call.respondRedirect("client/index.html")
|
||||
}
|
||||
}
|
||||
get {
|
||||
call.respondRedirect("client")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
server/src/main/java/dev/inmo/postssystem/server/Config.kt
Normal file
21
server/src/main/java/dev/inmo/postssystem/server/Config.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.File
|
||||
|
||||
@Serializable
|
||||
data class Config(
|
||||
val host: String = "0.0.0.0",
|
||||
val port: Int = 8080,
|
||||
@SerialName("database")
|
||||
val databaseConfig: DatabaseConfig = DatabaseConfig(),
|
||||
@SerialName("auth")
|
||||
val authConfig: AuthConfig = AuthConfig(),
|
||||
val clientStatic: String? = null,
|
||||
val filesFolder: String,
|
||||
val debugMode: Boolean = false
|
||||
) {
|
||||
val filesFolderFile: File
|
||||
get() = File(filesFolder)
|
||||
}
|
169
server/src/main/java/dev/inmo/postssystem/server/DI.kt
Normal file
169
server/src/main/java/dev/inmo/postssystem/server/DI.kt
Normal file
@ -0,0 +1,169 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import dev.inmo.postssystem.features.auth.server.AuthenticationRoutingConfigurator
|
||||
import dev.inmo.postssystem.features.auth.server.SessionAuthenticationConfigurator
|
||||
import dev.inmo.postssystem.features.auth.server.tokens.*
|
||||
import dev.inmo.postssystem.features.common.common.getAllDistinct
|
||||
import dev.inmo.postssystem.features.common.common.singleWithBinds
|
||||
import dev.inmo.postssystem.features.common.server.sessions.ApplicationAuthenticationConfigurator
|
||||
import dev.inmo.postssystem.features.files.common.*
|
||||
import dev.inmo.postssystem.features.files.common.storage.*
|
||||
import dev.inmo.postssystem.features.files.common.storage.WriteFilesStorage
|
||||
import dev.inmo.postssystem.features.files.server.FilesRoutingConfigurator
|
||||
import dev.inmo.postssystem.features.roles.common.*
|
||||
import dev.inmo.postssystem.features.roles.common.keyvalue.KeyValuesUsersRolesOriginalRepo
|
||||
import dev.inmo.postssystem.features.roles.manager.common.RolesManagerRole
|
||||
import dev.inmo.postssystem.features.roles.manager.common.RolesManagerRoleStorage
|
||||
import dev.inmo.postssystem.features.roles.manager.server.RolesManagerRolesChecker
|
||||
import dev.inmo.postssystem.features.roles.manager.server.RolesManagerUsersRolesStorageServerRoutesConfigurator
|
||||
import dev.inmo.postssystem.features.roles.server.*
|
||||
import dev.inmo.postssystem.features.status.server.StatusRoutingConfigurator
|
||||
import dev.inmo.postssystem.features.users.common.ExposedUsersStorage
|
||||
import dev.inmo.postssystem.features.users.server.UsersStorageServerRoutesConfigurator
|
||||
import dev.inmo.micro_utils.coroutines.LinkedSupervisorScope
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.*
|
||||
import dev.inmo.micro_utils.ktor.server.createKtorServer
|
||||
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedOneToManyKeyValueRepo
|
||||
import io.ktor.application.featureOrNull
|
||||
import io.ktor.application.log
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.netty.Netty
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.serialization.StringFormat
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.core.parameter.ParametersHolder
|
||||
import org.koin.core.qualifier.StringQualifier
|
||||
import org.koin.dsl.*
|
||||
import java.io.File
|
||||
|
||||
private fun Route.print() {
|
||||
println(this)
|
||||
children.forEach {
|
||||
it.print()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getDIModule(
|
||||
vararg args: String,
|
||||
baseScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
): Module {
|
||||
val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
val config = json.decodeFromString(Config.serializer(), File(args.first()).readText())
|
||||
|
||||
val originalFilesMetasKeyValueRepoQualifier = StringQualifier("OriginalFilesMetaKV")
|
||||
val filesMetasKeyValueRepoQualifier = StringQualifier("FilesMetaKV")
|
||||
val filesFolderQualifier = StringQualifier("filesFolder")
|
||||
val reviewVersionsQualifier = StringQualifier("reviewVersions")
|
||||
val releaseVersionsQualifier = StringQualifier("releaseVersions")
|
||||
val usersRolesKeyValueFactoryQualifier = StringQualifier("usersRolesKeyValueFactory")
|
||||
|
||||
return module {
|
||||
single { json }
|
||||
single<StringFormat> { get<Json>() }
|
||||
singleWithBinds { config }
|
||||
singleWithBinds { get<Config>().databaseConfig }
|
||||
singleWithBinds { get<Config>().authConfig }
|
||||
singleWithBinds(filesFolderQualifier) { get<Config>().filesFolderFile }
|
||||
|
||||
singleWithBinds { get<DatabaseConfig>().database }
|
||||
singleWithBinds(originalFilesMetasKeyValueRepoQualifier) {
|
||||
ExposedKeyValueRepo(get(), { text("fileid") }, { text("metaInfo") }, "FileIdsToMetas")
|
||||
}
|
||||
singleWithBinds(filesMetasKeyValueRepoQualifier) {
|
||||
MetasKeyValueRepo(
|
||||
get(),
|
||||
get(originalFilesMetasKeyValueRepoQualifier)
|
||||
)
|
||||
}
|
||||
single<FilesStorage> { DiskFilesStorage(get(filesFolderQualifier), get(filesMetasKeyValueRepoQualifier)) }
|
||||
single<WriteFilesStorage> { WriteDistFilesStorage(get(filesFolderQualifier), get(filesMetasKeyValueRepoQualifier)) }
|
||||
single<FullFilesStorage> { DefaultFullFilesStorage(get(), get()) }
|
||||
singleWithBinds { ExposedUsersStorage(get()) }
|
||||
singleWithBinds { exposedUsersAuthenticator(get(), get()) }
|
||||
|
||||
factory<KeyValuesUsersRolesOriginalRepo>(usersRolesKeyValueFactoryQualifier) { (tableName: String) ->
|
||||
ExposedOneToManyKeyValueRepo(get(), { long("userId") }, { text("role") }, tableName)
|
||||
}
|
||||
single {
|
||||
RolesManagerRoleStorage(get(usersRolesKeyValueFactoryQualifier) { ParametersHolder(mutableListOf("rolesManager")) })
|
||||
}
|
||||
single<UsersRolesStorage<RolesManagerRole>>(StringQualifier("RolesManagerRoleStorage")) { get<RolesManagerRoleStorage>() }
|
||||
singleWithBinds {
|
||||
UsersRolesStorageHolder(
|
||||
RolesManagerRole::class,
|
||||
get<RolesManagerRoleStorage>()
|
||||
)
|
||||
}
|
||||
singleWithBinds<UsersRolesStorage<UserRole>> { UsersRolesAggregator(getAll()) }
|
||||
|
||||
// Roles checkers
|
||||
single<RolesChecker<UserRole>>(StringQualifier(RolesManagerRolesChecker.key)) { RolesManagerRolesChecker }
|
||||
|
||||
factory<CoroutineScope> { baseScope.LinkedSupervisorScope() }
|
||||
|
||||
// Routing configurators
|
||||
singleWithBinds { FilesRoutingConfigurator(get(), null) }
|
||||
singleWithBinds { StatusRoutingConfigurator }
|
||||
singleWithBinds { UsersStorageServerRoutesConfigurator(get()) }
|
||||
singleWithBinds { UsersRolesStorageReadServerRoutesConfigurator<UserRole>(get(), UserRoleSerializer) }
|
||||
singleWithBinds { RolesManagerUsersRolesStorageServerRoutesConfigurator(get()) }
|
||||
|
||||
singleWithBinds { ClientStaticRoutingConfiguration(get<Config>().clientStatic) }
|
||||
singleWithBinds {
|
||||
UsersRolesAuthenticationConfigurator<UserRole>(
|
||||
get(),
|
||||
get(),
|
||||
getAll()
|
||||
)
|
||||
}
|
||||
|
||||
// Session and auth configurators
|
||||
singleWithBinds { SessionAuthenticationConfigurator(get<AuthConfig>().sessionAge) }
|
||||
singleWithBinds {
|
||||
DefaultAuthTokensService(
|
||||
get<Database>(),
|
||||
get(),
|
||||
get(),
|
||||
get<AuthConfig>().sessionAge,
|
||||
get()
|
||||
)
|
||||
}
|
||||
singleWithBinds { AuthenticationRoutingConfigurator(get(), get()) }
|
||||
|
||||
if (config.debugMode) {
|
||||
single<ApplicationRoutingConfigurator.Element>(StringQualifier("Tracer")) { ApplicationRoutingConfigurator.Element {(this as Routing).trace { application.log.trace(it.buildText()) } } }
|
||||
}
|
||||
|
||||
singleWithBinds { ApplicationRoutingConfigurator(getAllDistinct()) }
|
||||
singleWithBinds { ApplicationCachingHeadersConfigurator(getAllDistinct()) }
|
||||
singleWithBinds { ApplicationSessionsConfigurator(getAllDistinct()) }
|
||||
singleWithBinds { StatusPagesConfigurator(getAllDistinct()) }
|
||||
singleWithBinds { ApplicationAuthenticationConfigurator(getAllDistinct()) }
|
||||
singleWithBinds { WebSocketsConfigurator }
|
||||
|
||||
factory<ApplicationEngine> {
|
||||
val config = get<Config>()
|
||||
createKtorServer(
|
||||
Netty,
|
||||
config.host,
|
||||
config.port
|
||||
) {
|
||||
getAllDistinct<KtorApplicationConfigurator>().also(::println).forEach {
|
||||
it.apply { configure() }
|
||||
}
|
||||
if (config.debugMode) {
|
||||
featureOrNull(Routing) ?.print()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
|
||||
const val defaultDatabaseParamsName = "defaultDatabase"
|
||||
inline val Map<String, Any>.database: Database?
|
||||
get() = (get(defaultDatabaseParamsName) as? DatabaseConfig) ?.database
|
||||
|
||||
@Serializable
|
||||
data class DatabaseConfig(
|
||||
val url: String = "jdbc:postgresql://localhost:5432/tablet",
|
||||
val driver: String = org.postgresql.Driver::class.qualifiedName!!,
|
||||
val username: String = "",
|
||||
val password: String = ""
|
||||
) {
|
||||
@Transient
|
||||
val database: Database = Database.connect(
|
||||
url,
|
||||
driver,
|
||||
username,
|
||||
password
|
||||
)
|
||||
}
|
||||
|
10
server/src/main/java/dev/inmo/postssystem/server/Entrance.kt
Normal file
10
server/src/main/java/dev/inmo/postssystem/server/Entrance.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
fun main(vararg args: String) {
|
||||
startKoin {
|
||||
modules(getDIModule(*args))
|
||||
}.koin.get<ApplicationEngine>().start(wait = true)
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package dev.inmo.postssystem.server
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.install
|
||||
import io.ktor.websocket.WebSockets
|
||||
|
||||
object WebSocketsConfigurator : KtorApplicationConfigurator {
|
||||
override fun Application.configure() {
|
||||
install(WebSockets)
|
||||
}
|
||||
}
|
10
server/test.config.json
Normal file
10
server/test.config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"url": "jdbc:postgresql://127.0.0.1:8090/test",
|
||||
"username": "test",
|
||||
"password": "test"
|
||||
},
|
||||
"clientStatic": "../client/build/distributions",
|
||||
"filesFolder": "/tmp/files",
|
||||
"debugMode": true
|
||||
}
|
Reference in New Issue
Block a user