full reborn

This commit is contained in:
2021-11-24 13:52:27 +06:00
parent 0ac6b0a4df
commit 6a6a197041
246 changed files with 4327 additions and 6952 deletions
.github/workflows
LICENSEREADME.mdbuild.gradle
business_cases/post_creating
client
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
business_cases
post_creating
main
common
src
commonMain
kotlin
dev
inmo
postssystem
main
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
business_cases
post_creating
client
core
defaultAndroidSettingsdefaultAndroidSettings.gradleextensions.gradle
features
auth
common
client
build.gradle
src
commonMain
kotlin
dev
inmo
main
common
build.gradle
src
commonMain
kotlin
dev
inmo
jvmMain
kotlin
dev
inmo
postssystem
features
main
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
features
files
client
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
main
common
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
features
roles
status
client
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
main
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
status
main
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
features
template
users
client
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
main
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
jvmMain
kotlin
dev
inmo
postssystem
features
main
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
gradle.properties
gradle/wrapper
gradlewgradlew.bat
mimes_generator
mppAndroidProject.gradlemppJavaProject.gradlemppJsProject.gradlemppProjectWithSerialization.gradlepubconf.kpsbpublish.gradlepublish.kpsb
publishing
server
settings.gradle

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

@ -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

@ -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")
}
}
}
}

@ -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)
}

@ -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
)
}

@ -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

@ -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
}