init
This commit is contained in:
commit
2e6fb71ae2
.gitignoreREADME.mdbuild.gradle.ktsdocker-compose.yamlgradle.properties
gradle/wrapper
settings.gradle.ktssrc
main
test/kotlin/cn/fadinglight
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Ktor 项目模板
|
||||||
|
|
||||||
|
> Kotlin + Gradle + Ktor + Exposed + Docker
|
||||||
|
|
||||||
|
配置好啦
|
||||||
|
|
||||||
|
- Database
|
||||||
|
- Logging
|
||||||
|
- Routing
|
||||||
|
- Session
|
||||||
|
- WebSocket
|
68
build.gradle.kts
Normal file
68
build.gradle.kts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
val ktor_version: String by project
|
||||||
|
val kotlin_version: String by project
|
||||||
|
val logback_version: String by project
|
||||||
|
val exposed_version: String by project
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
application
|
||||||
|
kotlin("jvm") version "1.7.20"
|
||||||
|
id("io.ktor.plugin") version "2.1.2"
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization") version "1.7.20"
|
||||||
|
id("com.github.johnrengelman.shadow") version "5.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
group = "cn.fadinglight"
|
||||||
|
version = "0.0.1"
|
||||||
|
application {
|
||||||
|
mainClass.set("cn.fadinglight.ApplicationKt")
|
||||||
|
|
||||||
|
val isDevelopment: Boolean = project.ext.has("development")
|
||||||
|
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.withType<Jar> {
|
||||||
|
manifest {
|
||||||
|
attributes(
|
||||||
|
mapOf(
|
||||||
|
"Main-Class" to application.mainClass
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven("https://maven.aliyun.com/repository/public/")
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Exposed ORM library
|
||||||
|
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
|
||||||
|
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
|
||||||
|
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
|
||||||
|
|
||||||
|
implementation("com.zaxxer:HikariCP:5.0.1") // JDBC Connection Pool
|
||||||
|
implementation("org.mariadb.jdbc:mariadb-java-client:3.0.8") // JDBC Connector for Mariadb
|
||||||
|
|
||||||
|
// Session
|
||||||
|
implementation("io.ktor:ktor-server-sessions:$ktor_version")
|
||||||
|
// Ktor
|
||||||
|
implementation("io.ktor:ktor-server-call-logging:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-server-compression-jvm:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-server-locations:$ktor_version")
|
||||||
|
|
||||||
|
|
||||||
|
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||||
|
implementation("io.ktor:ktor-server-websockets-jvm:2.1.1")
|
||||||
|
implementation("io.ktor:ktor-server-locations-jvm:2.1.1")
|
||||||
|
|
||||||
|
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||||
|
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||||
|
}
|
19
docker-compose.yaml
Normal file
19
docker-compose.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mariadb
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MARIADB_ROOT_PASSWORD: 123456
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
volumes:
|
||||||
|
- db-data:/var/lib/mysql
|
||||||
|
|
||||||
|
adminer:
|
||||||
|
image: adminer
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8081:8080
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-data:
|
5
gradle.properties
Normal file
5
gradle.properties
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
ktor_version=2.1.2
|
||||||
|
kotlin_version=1.7.20
|
||||||
|
logback_version=1.4.1
|
||||||
|
kotlin.code.style=official
|
||||||
|
exposed_version=0.39.2
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.5-all.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
1
settings.gradle.kts
Normal file
1
settings.gradle.kts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = "cn.fadinglight.bill-ktor"
|
18
src/main/kotlin/cn/fadinglight/Application.kt
Normal file
18
src/main/kotlin/cn/fadinglight/Application.kt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package cn.fadinglight
|
||||||
|
|
||||||
|
import cn.fadinglight.plugins.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
|
||||||
|
|
||||||
|
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
|
||||||
|
|
||||||
|
fun Application.module() {
|
||||||
|
configureAdministration()
|
||||||
|
configureSerialization()
|
||||||
|
configureHTTP()
|
||||||
|
configureLogging()
|
||||||
|
configureRouting()
|
||||||
|
configureSession()
|
||||||
|
configureWebSocket()
|
||||||
|
configureDatabase()
|
||||||
|
}
|
19
src/main/kotlin/cn/fadinglight/mapers/Schemas.kt
Normal file
19
src/main/kotlin/cn/fadinglight/mapers/Schemas.kt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package cn.fadinglight.mapers
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
|
||||||
|
object Bills : IntIdTable() {
|
||||||
|
val type = integer("type")
|
||||||
|
val date = varchar("date", 15)
|
||||||
|
val money = integer("money")
|
||||||
|
val cls = varchar("cls", 31)
|
||||||
|
val label = varchar("label", 31)
|
||||||
|
val options = varchar("options", 255)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Labels : IntIdTable() {
|
||||||
|
val name = varchar("name", 15)
|
||||||
|
val type = integer("type")
|
||||||
|
val relativeId = integer("relative_id")
|
||||||
|
val nums = integer("nums")
|
||||||
|
}
|
15
src/main/kotlin/cn/fadinglight/models/Bill.kt
Normal file
15
src/main/kotlin/cn/fadinglight/models/Bill.kt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package cn.fadinglight.models
|
||||||
|
|
||||||
|
enum class BillType {
|
||||||
|
Consume,
|
||||||
|
Income,
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Bill(
|
||||||
|
var id: Int?,
|
||||||
|
var type: BillType,
|
||||||
|
val money: Int,
|
||||||
|
val cls: String,
|
||||||
|
val label: String,
|
||||||
|
val options: String?,
|
||||||
|
)
|
14
src/main/kotlin/cn/fadinglight/plugins/Administration.kt
Normal file
14
src/main/kotlin/cn/fadinglight/plugins/Administration.kt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import io.ktor.server.engine.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
|
||||||
|
fun Application.configureAdministration() {
|
||||||
|
install(ShutDownUrl.ApplicationCallPlugin) {
|
||||||
|
// The URL that will be intercepted (you can also use the application.conf's ktor.deployment.shutdown.url key)
|
||||||
|
shutDownUrl = "/ktor/application/shutdown"
|
||||||
|
// A function that will be executed to get the exit code of the process
|
||||||
|
exitCodeSupplier = { 0 } // ApplicationCall.() -> Int
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/main/kotlin/cn/fadinglight/plugins/Database.kt
Normal file
29
src/main/kotlin/cn/fadinglight/plugins/Database.kt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import cn.fadinglight.Bills
|
||||||
|
import cn.fadinglight.Labels
|
||||||
|
import com.zaxxer.hikari.HikariConfig
|
||||||
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
import org.jetbrains.exposed.sql.SchemaUtils
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
const val HIKARI_CONFIG_KEY = "ktor.hikariConfig"
|
||||||
|
|
||||||
|
fun Application.configureDatabase() {
|
||||||
|
val configPath = environment.config.property(HIKARI_CONFIG_KEY).getString()
|
||||||
|
val dbConfig = HikariConfig(configPath)
|
||||||
|
val dataSource = HikariDataSource(dbConfig)
|
||||||
|
Database.connect(dataSource)
|
||||||
|
createTables()
|
||||||
|
log.info("Initialized database")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTables() = transaction {
|
||||||
|
SchemaUtils.create(
|
||||||
|
Bills,
|
||||||
|
Labels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
17
src/main/kotlin/cn/fadinglight/plugins/HTTP.kt
Normal file
17
src/main/kotlin/cn/fadinglight/plugins/HTTP.kt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import io.ktor.server.plugins.compression.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
|
||||||
|
fun Application.configureHTTP() {
|
||||||
|
install(Compression) {
|
||||||
|
gzip {
|
||||||
|
priority = 1.0
|
||||||
|
}
|
||||||
|
deflate {
|
||||||
|
priority = 10.0
|
||||||
|
minimumSize(1024) // condition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/main/kotlin/cn/fadinglight/plugins/Logging.kt
Normal file
11
src/main/kotlin/cn/fadinglight/plugins/Logging.kt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.plugins.callloging.*
|
||||||
|
import org.slf4j.event.Level
|
||||||
|
|
||||||
|
fun Application.configureLogging() {
|
||||||
|
install(CallLogging) {
|
||||||
|
level = Level.INFO
|
||||||
|
}
|
||||||
|
}
|
15
src/main/kotlin/cn/fadinglight/plugins/Routing.kt
Normal file
15
src/main/kotlin/cn/fadinglight/plugins/Routing.kt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import cn.fadinglight.routes.billRoute
|
||||||
|
import cn.fadinglight.routes.labelRoute
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
|
fun Application.configureRouting() {
|
||||||
|
routing {
|
||||||
|
route("/api/v1") {
|
||||||
|
billRoute()
|
||||||
|
labelRoute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/main/kotlin/cn/fadinglight/plugins/Serialization.kt
Normal file
11
src/main/kotlin/cn/fadinglight/plugins/Serialization.kt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
|
||||||
|
fun Application.configureSerialization() {
|
||||||
|
install(ContentNegotiation) {
|
||||||
|
json()
|
||||||
|
}
|
||||||
|
}
|
23
src/main/kotlin/cn/fadinglight/plugins/Session.kt
Normal file
23
src/main/kotlin/cn/fadinglight/plugins/Session.kt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import io.ktor.server.sessions.*
|
||||||
|
|
||||||
|
fun Application.configureSession() {
|
||||||
|
install(Sessions) {
|
||||||
|
cookie<UserSession>("user_session", SessionStorageMemory()) {
|
||||||
|
cookie.path = "/"
|
||||||
|
cookie.maxAgeInSeconds = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routing {
|
||||||
|
get("/sessions") {
|
||||||
|
call.sessions.set(UserSession(id = "0", count = 0))
|
||||||
|
call.respondText("Hello world!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UserSession(val id: String, val count: Int)
|
37
src/main/kotlin/cn/fadinglight/plugins/WebSocket.kt
Normal file
37
src/main/kotlin/cn/fadinglight/plugins/WebSocket.kt
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package cn.fadinglight.plugins
|
||||||
|
|
||||||
|
import io.ktor.serialization.kotlinx.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import io.ktor.server.websocket.*
|
||||||
|
import io.ktor.websocket.*
|
||||||
|
import kotlinx.coroutines.channels.consumeEach
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
fun Application.configureWebSocket() {
|
||||||
|
install(WebSockets) {
|
||||||
|
contentConverter = KotlinxWebsocketSerializationConverter(Json)
|
||||||
|
|
||||||
|
pingPeriod = Duration.ofSeconds(15)
|
||||||
|
timeout = Duration.ofSeconds(15)
|
||||||
|
maxFrameSize = Long.MAX_VALUE
|
||||||
|
masking = false
|
||||||
|
}
|
||||||
|
|
||||||
|
routing {
|
||||||
|
webSocket("/customers/1") {
|
||||||
|
sendSerialized(Customer(id = 1, firstName = "John", lastName = "Smith"))
|
||||||
|
incoming.consumeEach {
|
||||||
|
when (it) {
|
||||||
|
is Frame.Text -> println(it.readText())
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Customer(val id: Int, val firstName: String, val lastName: String)
|
13
src/main/kotlin/cn/fadinglight/routes/BillRoutes.kt
Normal file
13
src/main/kotlin/cn/fadinglight/routes/BillRoutes.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package cn.fadinglight.routes
|
||||||
|
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
|
|
||||||
|
fun Route.billRoute() {
|
||||||
|
route("/bill") {
|
||||||
|
get("/") {}
|
||||||
|
get("/{year}/{month}") {}
|
||||||
|
post("/create") {}
|
||||||
|
delete("/{year}/{month}/{day}") {}
|
||||||
|
}
|
||||||
|
}
|
9
src/main/kotlin/cn/fadinglight/routes/LabelRoutes.kt
Normal file
9
src/main/kotlin/cn/fadinglight/routes/LabelRoutes.kt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package cn.fadinglight.routes
|
||||||
|
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
|
fun Route.labelRoute() {
|
||||||
|
route("/label") {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
src/main/kotlin/cn/fadinglight/services/BillService.kt
Normal file
15
src/main/kotlin/cn/fadinglight/services/BillService.kt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package cn.fadinglight.services
|
||||||
|
|
||||||
|
import cn.fadinglight.models.Bill
|
||||||
|
|
||||||
|
interface BillService {
|
||||||
|
fun getMonthBills(year: String, month: String): List<Bill>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BillServiceImpl : BillService {
|
||||||
|
override fun getMonthBills(year: String, month: String): List<Bill> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
15
src/main/resources/application.conf
Normal file
15
src/main/resources/application.conf
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
ktor {
|
||||||
|
development = true
|
||||||
|
|
||||||
|
deployment {
|
||||||
|
port = 8080
|
||||||
|
port = ${?PORT}
|
||||||
|
watch = [ bill-ktor ]
|
||||||
|
}
|
||||||
|
|
||||||
|
hikariConfig = "/db.properties"
|
||||||
|
|
||||||
|
application {
|
||||||
|
modules = [ cn.fadinglight.ApplicationKt.module ]
|
||||||
|
}
|
||||||
|
}
|
4
src/main/resources/db.properties
Normal file
4
src/main/resources/db.properties
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
driverClassName=org.mariadb.jdbc.Driver
|
||||||
|
jdbcUrl=jdbc:mariadb://localhost:3306/bill
|
||||||
|
dataSource.user=root
|
||||||
|
dataSource.password=123456
|
12
src/main/resources/logback.xml
Normal file
12
src/main/resources/logback.xml
Normal file
|
@ -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="trace">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
<logger name="io.netty" level="INFO"/>
|
||||||
|
<logger name="com.zaxxer.hikari" level="ERROR"/>
|
||||||
|
</configuration>
|
20
src/test/kotlin/cn/fadinglight/ApplicationTest.kt
Normal file
20
src/test/kotlin/cn/fadinglight/ApplicationTest.kt
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package cn.fadinglight
|
||||||
|
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import io.ktor.server.testing.*
|
||||||
|
import cn.fadinglight.plugins.*
|
||||||
|
|
||||||
|
class ApplicationTest {
|
||||||
|
@Test
|
||||||
|
fun testRoot() = testApplication {
|
||||||
|
application {
|
||||||
|
configureRouting()
|
||||||
|
}
|
||||||
|
client.get("/").apply {
|
||||||
|
assertEquals(HttpStatusCode.OK, status)
|
||||||
|
// assertEquals("Hello World!", bodyAsText())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user