Skip to content

Commit

Permalink
Merge pull request #1 from groupeminaste/feature/ktor-setup
Browse files Browse the repository at this point in the history
Rebuilding the backend with Ktor clean architecture
  • Loading branch information
nathanfallet committed Dec 6, 2023
2 parents 1aa221f + b62b013 commit f07a37a
Show file tree
Hide file tree
Showing 85 changed files with 2,762 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [ GroupeMINASTE, NathanFallet, PlugNPush, JohnPoliakov ]
21 changes: 21 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up our JDK environment
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 19
- name: Install dependencies and run tests
run: ./gradlew jvmTest koverXmlReport
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.gradle
.idea
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
local.properties

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store

### JS ###
yarn.lock

### Keys
*.pem
*.p8
25 changes: 25 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
kotlin("jvm").version("1.9.20").apply(false)
kotlin("plugin.serialization").version("1.9.20").apply(false)
id("convention.publication").apply(false)
id("org.jetbrains.kotlinx.kover").version("0.7.4").apply(false)
id("com.google.devtools.ksp").version("1.9.20-1.0.13").apply(false)
id("dev.petuska.npm.publish").version("3.4.1").apply(false)
}

allprojects {
group = "me.nathanfallet.extopy"
version = "0.1.0"

repositories {
mavenCentral()
}

dependencies {
configurations
.filter { it.name.startsWith("ksp") && it.name.contains("Test") }
.forEach {
add(it.name, "io.mockative:mockative-processor:2.0.1")
}
}
}
2 changes: 2 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignore:
- "extopy-commons/src/commonMain/kotlin/me/nathanfallet/extopy/models"
26 changes: 26 additions & 0 deletions contribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Contribution guide

## Contribute to code

Task are tracked using GitHub issues.

Contributions are submitted with Pull Requests. Please assign yourself to an issue when you start working on it, so that
we know someone is already working on it, to avoid duplicated contributions.

Repositories:

- [groupeminaste/extopy-backend](https://github.com/groupeminaste/extopy-backend): Backend and API of Extopy
- [groupeminaste/extopy-app](https://github.com/groupeminaste/extopy-app): Mobile apps of Extopy, with a shared module
using KMM

Recommended tools for development:

- Server side: IntelliJ or VS Code with Kotlin and Gradle extensions
- Android app: IntelliJ or Android Studio
- iOS app: Xcode with gradle installed through Homebrew

## Contribute to translations

Base strings (english) are added directly to the repository.

Translations are filled using [Weblate](https://weblate.groupe-minaste.org/projects/extopy/).
7 changes: 7 additions & 0 deletions convention-plugins/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
`kotlin-dsl`
}

repositories {
gradlePluginPortal()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import java.util.*

plugins {
`maven-publish`
signing
}

// Stub secrets to let the project sync and build without the publication values set up
ext["signing.keyId"] = null
ext["signing.password"] = null
ext["signing.secretKeyRingFile"] = null
ext["ossrhUsername"] = null
ext["ossrhPassword"] = null

// Grabbing secrets from local.properties file or from environment variables, which could be used on CI
val secretPropsFile = project.rootProject.file("local.properties")
if (secretPropsFile.exists()) {
secretPropsFile.reader().use {
Properties().apply {
load(it)
}
}.onEach { (name, value) ->
ext[name.toString()] = value
}
} else {
ext["signing.keyId"] = System.getenv("SIGNING_KEY_ID")
ext["signing.password"] = System.getenv("SIGNING_PASSWORD")
ext["signing.secretKeyRingFile"] = System.getenv("SIGNING_SECRET_KEY_RING_FILE")
ext["ossrhUsername"] = System.getenv("OSSRH_USERNAME")
ext["ossrhPassword"] = System.getenv("OSSRH_PASSWORD")
}

val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}

fun getExtraString(name: String) = ext[name]?.toString()

publishing {
// Configure maven central repository
repositories {
maven {
name = "sonatype"
setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = getExtraString("ossrhUsername")
password = getExtraString("ossrhPassword")
}
}
}

// Configure all publications
publications.withType<MavenPublication> {
// Stub javadoc.jar artifact
artifact(javadocJar.get())

// Provide artifacts information requited by Maven Central
pom {
url.set("https://github.com/groupeminaste/extopy-backend")

licenses {
license {
name.set("GPL-3.0")
url.set("https://opensource.org/licenses/GPL-3.0")
}
}
developers {
developer {
id.set("NathanFallet")
name.set("Nathan Fallet")
email.set("contact@nathanfallet.me")
url.set("https://www.nathanfallet.me")
}
}
scm {
url.set("https://github.com/groupeminaste/extopy-backend.git")
}
}
}
}

// Signing artifacts. Signing.* extra properties values will be used
signing {
sign(publishing.publications)
}

// The Gradle Nexus plugin has a bug that requires us to manually
// configure the signing tasks to run before the publishing tasks.
// see: https://github.com/gradle-nexus/publish-plugin/issues/208
val signingTasks: TaskCollection<Sign> = tasks.withType<Sign>()
tasks.withType<PublishToMavenRepository>().configureEach {
mustRunAfter(signingTasks)
}
91 changes: 91 additions & 0 deletions extopy-backend/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("application")
id("org.jetbrains.kotlinx.kover")
id("com.google.devtools.ksp")
}

application {
mainClass.set("me.nathanfallet.extopy.ApplicationKt")
}

kotlin {
jvm {
jvmToolchain(19)
withJava()
testRuns.named("test") {
executionTask.configure {
useJUnitPlatform()
}
}
}

applyDefaultHierarchyTemplate()

val coroutinesVersion = "1.7.3"
val ktorVersion = "2.3.6"
val koinVersion = "3.5.0"
val exposedVersion = "0.40.1"
val logbackVersion = "0.9.30"
val ktorxVersion = "1.6.1"

sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-call-logging:$ktorVersion")
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
implementation("io.ktor:ktor-server-auth:$ktorVersion")
implementation("io.ktor:ktor-server-auth-jwt:$ktorVersion")
implementation("io.ktor:ktor-server-sessions:$ktorVersion")
implementation("io.ktor:ktor-server-websockets:$ktorVersion")
implementation("io.ktor:ktor-server-freemarker:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-apache:$ktorVersion")
implementation("io.ktor:ktor-client-jetty:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")

implementation("io.insert-koin:koin-core:$koinVersion")
implementation("io.insert-koin:koin-ktor:$koinVersion")

implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")

implementation("ch.qos.logback:logback-core:$logbackVersion")
implementation("ch.qos.logback:logback-classic:$logbackVersion")
implementation("org.slf4j:slf4j-api:2.0.7")

implementation("me.nathanfallet.i18n:i18n:1.0.4")
implementation("me.nathanfallet.ktorx:ktor-i18n:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-i18n-freemarker:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-routers:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-routers-locale:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-sentry:$ktorxVersion")
implementation("me.nathanfallet.cloudflare:cloudflare-api-client:4.0.7")

implementation("com.mysql:mysql-connector-j:8.0.33")
implementation("at.favre.lib:bcrypt:0.9.0")
implementation("org.apache.commons:commons-email:1.5")
implementation("io.sentry:sentry:6.34.0")

api(project(":extopy-commons"))
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("io.ktor:ktor-server-test-host:$ktorVersion")
implementation("io.mockative:mockative:2.0.1")
implementation("io.mockk:mockk:1.13.8")
implementation("org.jsoup:jsoup:1.16.2")
implementation("com.h2database:h2:2.2.224")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package me.nathanfallet.extopy

import io.ktor.server.application.*
import io.ktor.server.netty.*
import me.nathanfallet.extopy.plugins.*

fun main(args: Array<String>): Unit = EngineMain.main(args)

fun Application.module() {
// Initialize plugins
configureI18n()
configureKoin()
configureSerialization()
configureSecurity()
configureSessions()
configureTemplating()
configureRouting()
configureSockets()

configureNotifications()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package me.nathanfallet.extopy.controllers.auth

import io.ktor.server.freemarker.*
import me.nathanfallet.extopy.models.auth.LoginPayload
import me.nathanfallet.extopy.models.auth.RegisterCodePayload
import me.nathanfallet.extopy.models.auth.RegisterPayload
import me.nathanfallet.ktorx.controllers.auth.IAuthWithCodeController
import me.nathanfallet.ktorx.models.auth.AuthMapping
import me.nathanfallet.ktorx.routers.auth.LocalizedAuthWithCodeTemplateRouter
import me.nathanfallet.ktorx.usecases.localization.IGetLocaleForCallUseCase

class AuthRouter(
controller: IAuthWithCodeController<LoginPayload, RegisterPayload, RegisterCodePayload>,
getLocaleForCallUseCase: IGetLocaleForCallUseCase,
) : LocalizedAuthWithCodeTemplateRouter<LoginPayload, RegisterPayload, RegisterCodePayload>(
LoginPayload::class,
RegisterPayload::class,
RegisterCodePayload::class,
AuthMapping(loginTemplate = "auth/login.ftl", registerTemplate = "auth/register.ftl"),
{ template, model -> respondTemplate(template, model) },
controller,
getLocaleForCallUseCase
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package me.nathanfallet.extopy.controllers.notifications

class NotificationsController {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package me.nathanfallet.extopy.controllers.notifications

class NotificationsRouter {
}
Loading

0 comments on commit f07a37a

Please sign in to comment.