full reborn
This commit is contained in:
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