Skip to content

Commit

Permalink
Add support for SimpleAnalytics as stats provider
Browse files Browse the repository at this point in the history
Signed-off-by: Till Kottmann <me@deletescape.ch>
  • Loading branch information
nyancrimew committed Oct 24, 2019
1 parent 2b0fb39 commit 7b281ba
Show file tree
Hide file tree
Showing 17 changed files with 441 additions and 59 deletions.
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Expand Up @@ -15,15 +15,22 @@ application {


dependencies { dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
compile("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.2")
compile("io.ktor:ktor-server-netty:$ktor_version") compile("io.ktor:ktor-server-netty:$ktor_version")
compile("ch.qos.logback:logback-classic:$logback_version") compile("ch.qos.logback:logback-classic:$logback_version")
compile("io.ktor:ktor-metrics:$ktor_version") compile("io.ktor:ktor-metrics:$ktor_version")
compile("io.ktor:ktor-server-core:$ktor_version") compile("io.ktor:ktor-server-core:$ktor_version")
compile("io.ktor:ktor-gson:$ktor_version") compile("io.ktor:ktor-gson:$ktor_version")
compile("io.ktor:ktor-client-core:$ktor_version")
compile("io.ktor:ktor-client-apache:$ktor_version")
compile("io.ktor:ktor-client-gson:$ktor_version")
compile("org.apache.httpcomponents:httpasyncclient:4.1.4")
compile(project(":pebble")) compile(project(":pebble"))


compile("com.github.zensum:ktor-health-check:011a5a8") compile("com.github.zensum:ktor-health-check:011a5a8")
compile("org.koin:koin-ktor:$koin_version") compile("org.koin:koin-ktor:$koin_version")
compile("net.jodah:expiringmap:0.5.9")
compile("com.github.ben-manes.caffeine:caffeine:2.8.0")
compile(project(":data:base")) compile(project(":data:base"))
compile(project(":commons")) compile(project(":commons"))


Expand Down
12 changes: 11 additions & 1 deletion app/resources/application.conf
Expand Up @@ -8,6 +8,8 @@ ktor {
} }
} }
dogbin { dogbin {
host = "localhost"
host = ${?HOST}
db { db {
location = "dogbin.xdb" location = "dogbin.xdb"
location = ${?DB_LOCATION} location = ${?DB_LOCATION}
Expand All @@ -20,4 +22,12 @@ dogbin {
session = "DEADBEEF" session = "DEADBEEF"
session = ${?SESSION_KEY} session = ${?SESSION_KEY}
} }
} stats {
enabled = true
enabled = ${?COLLECT_STATS}

// Whether to use SimpleAnalytics for stats (querying SA for view count and reporting redirects to SA)
useSA = false
useSA = ${?SIMPLEANALYTICS}
}
}
1 change: 1 addition & 0 deletions app/resources/templates/index.peb
Expand Up @@ -83,4 +83,5 @@
var app = new haste(); var app = new haste();
}); });
</script> </script>
{{ stats_embed | raw }}
{% endblock %} {% endblock %}
30 changes: 17 additions & 13 deletions app/resources/templates/user.peb
Expand Up @@ -12,21 +12,25 @@
<div id="content"> <div id="content">
<div class="center-inside"> <div class="center-inside">
<div class="card"> <div class="card">
<h1>{{ user.username }}</h1> <h1>{{ user.username }}</h1>
<p>Joined: {{ user.created }}</p> <p>Joined: {{ user.created }}</p>
{% if pastes is not empty %} {% if pastes is not empty %}
<h2>Documents</h2> <h2>Documents</h2>
{% for paste in pastes %} {% for paste in pastes %}
<p><a href="/{{ paste.slug }}">{{ paste.slug }}</a> ({{ paste.created }})</p> <p><a href="/{{ paste.slug }}">{{ paste.slug }}</a> ({{ paste.created }})</p>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
<a class="md-btn md-btn-secondary left" href="/me/changepass"><span>CHANGE PASSWORD</span></a> <a class="md-btn md-btn-secondary left" href="/me/changepass"><span>CHANGE PASSWORD</span></a>
<a class="md-btn md-btn-secondary right" href="/logout"><span>LOG OUT</span></a> <a class="md-btn md-btn-secondary right" href="/logout"><span>LOG OUT</span></a>
</code></div> </code></div>
</div> </div>
</div> </div>
<div id="footer" class="unselectable"> <div id="footer" class="unselectable">
<div id="copyright">&copy; {{ year }} <a href="https://deletescape.ch" target="_blank" rel="noopener">deletescape</a> <div id="copyright">&copy; {{ year }} <a href="https://deletescape.ch" target="_blank"
rel="noopener">deletescape</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block after_body %}
{{ stats_embed | raw }}
{% endblock %}
15 changes: 15 additions & 0 deletions app/src/dog/del/app/Application.kt
Expand Up @@ -8,12 +8,18 @@ import dog.del.app.frontend.legacyApi
import dog.del.app.session.ApiSession import dog.del.app.session.ApiSession
import dog.del.app.session.WebSession import dog.del.app.session.WebSession
import dog.del.app.session.XdSessionStorage import dog.del.app.session.XdSessionStorage
import dog.del.app.stats.StatisticsReporter
import dog.del.app.utils.DogbinPebbleExtension import dog.del.app.utils.DogbinPebbleExtension
import dog.del.commons.keygen.KeyGenerator import dog.del.commons.keygen.KeyGenerator
import dog.del.commons.keygen.PhoneticKeyGenerator import dog.del.commons.keygen.PhoneticKeyGenerator
import dog.del.data.base.Database import dog.del.data.base.Database
import dog.del.data.base.model.config.Config import dog.del.data.base.model.config.Config
import io.ktor.application.* import io.ktor.application.*
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.client.features.json.GsonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.features.* import io.ktor.features.*
import io.ktor.routing.* import io.ktor.routing.*
import io.ktor.gson.* import io.ktor.gson.*
Expand Down Expand Up @@ -60,6 +66,15 @@ fun Application.module(testing: Boolean = false) {
single { Database.init(appConfig.db.location, appConfig.db.environment) } single { Database.init(appConfig.db.location, appConfig.db.environment) }
single { Config.getConfig(get()) } single { Config.getConfig(get()) }
single<KeyGenerator> { PhoneticKeyGenerator() } single<KeyGenerator> { PhoneticKeyGenerator() }
single { StatisticsReporter.getReporter(appConfig) }
single { this@module.log }
single {
HttpClient(Apache) {
install(JsonFeature) {
serializer = GsonSerializer()
}
}
}
} }
modules( modules(
appModule appModule
Expand Down
8 changes: 8 additions & 0 deletions app/src/dog/del/app/config/AppConfig.kt
Expand Up @@ -6,6 +6,8 @@ import java.io.File




class AppConfig(config: ApplicationConfig) { class AppConfig(config: ApplicationConfig) {
val host = config.property("dogbin.host").getString()

val db = DbConfig( val db = DbConfig(
location = File(config.property("dogbin.db.location").getString()), location = File(config.property("dogbin.db.location").getString()),
environment = config.property("dogbin.db.environment").getString() environment = config.property("dogbin.db.environment").getString()
Expand All @@ -15,6 +17,12 @@ class AppConfig(config: ApplicationConfig) {
session = hex(config.property("dogbin.keys.session").getString()) session = hex(config.property("dogbin.keys.session").getString())
) )


val stats = Stats(
enabled = config.property("dogbin.stats.enabled").getString().toBoolean(),
useSA = config.property("dogbin.stats.useSA").getString().toBoolean()
)

data class DbConfig(val location: File, val environment: String) data class DbConfig(val location: File, val environment: String)
data class Keys(val session: ByteArray) data class Keys(val session: ByteArray)
data class Stats(val enabled: Boolean, val useSA: Boolean)
} }
29 changes: 19 additions & 10 deletions app/src/dog/del/app/dto/DocumentDto.kt
@@ -1,12 +1,14 @@
package dog.del.app.dto package dog.del.app.dto


import dog.del.app.stats.StatisticsReporter
import dog.del.commons.format import dog.del.commons.format
import dog.del.commons.formatLong import dog.del.commons.formatLong
import dog.del.commons.formatShort import dog.del.commons.formatShort
import dog.del.commons.lineCount import dog.del.commons.lineCount
import dog.del.data.base.model.document.XdDocumentType import dog.del.data.base.model.document.XdDocumentType
import dog.del.data.model.Document import dog.del.data.model.Document
import dog.del.data.model.DocumentType import dog.del.data.model.DocumentType
import kotlinx.coroutines.runBlocking
import java.util.* import java.util.*


data class FrontendDocumentDto( data class FrontendDocumentDto(
Expand All @@ -24,15 +26,22 @@ data class FrontendDocumentDto(
// Use frontmatter data for rendered markdown content // Use frontmatter data for rendered markdown content
val description = content?.take(100) ?: "The sexiest pastebin and url-shortener ever" val description = content?.take(100) ?: "The sexiest pastebin and url-shortener ever"
val title = "dogbin - $slug" val title = "dogbin - $slug"

companion object { companion object {
fun fromDocument(document: Document<XdDocumentType, *>, locale: Locale? = null) = FrontendDocumentDto( fun fromDocument(
document.slug, document: Document<XdDocumentType, *>,
DocumentTypeDto.fromXdDocumentType(document.type), reporter: StatisticsReporter? = null,
document.stringContent, locale: Locale? = null
UserDto.fromUser(document.owner), ) = runBlocking {
document.created.formatShort(locale), FrontendDocumentDto(
document.viewCount document.slug,
) DocumentTypeDto.fromXdDocumentType(document.type),
document.stringContent,
UserDto.fromUser(document.owner),
document.created.formatShort(locale),
reporter?.getImpressions(document.slug) ?: document.viewCount
)
}
} }
} }


Expand All @@ -47,11 +56,11 @@ data class CreateDocumentResponseDto(
val message: String? = null val message: String? = null
) )


enum class DocumentTypeDto{ enum class DocumentTypeDto {
URL, PASTE; URL, PASTE;


companion object { companion object {
fun fromXdDocumentType(documentType: XdDocumentType) = when(documentType) { fun fromXdDocumentType(documentType: XdDocumentType) = when (documentType) {
XdDocumentType.PASTE -> PASTE XdDocumentType.PASTE -> PASTE
XdDocumentType.URL -> URL XdDocumentType.URL -> URL
else -> throw IllegalStateException() else -> throw IllegalStateException()
Expand Down
28 changes: 17 additions & 11 deletions app/src/dog/del/app/frontend/Index.kt
Expand Up @@ -3,31 +3,33 @@ package dog.del.app.frontend
import dog.del.app.dto.FrontendDocumentDto import dog.del.app.dto.FrontendDocumentDto
import dog.del.app.session.session import dog.del.app.session.session
import dog.del.app.session.user import dog.del.app.session.user
import dog.del.app.stats.StatisticsReporter
import dog.del.app.stats.StatisticsReporter.*
import dog.del.app.utils.locale import dog.del.app.utils.locale
import dog.del.app.utils.slug import dog.del.app.utils.slug
import dog.del.commons.Date
import dog.del.commons.year
import dog.del.data.base.Database
import dog.del.data.base.model.document.XdDocument import dog.del.data.base.model.document.XdDocument
import dog.del.data.base.model.document.XdDocumentType import dog.del.data.base.model.document.XdDocumentType
import dog.del.data.model.DocumentType
import io.ktor.application.call import io.ktor.application.call
import io.ktor.pebble.respondTemplate import io.ktor.pebble.respondTemplate
import io.ktor.response.respondRedirect import io.ktor.response.respondRedirect
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
import io.ktor.routing.route import io.ktor.routing.route
import jetbrains.exodus.database.TransientEntityStore import jetbrains.exodus.database.TransientEntityStore
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.koin.ktor.ext.inject import org.koin.ktor.ext.inject
import java.util.*


fun Route.index() = route("/") { fun Route.index() = route("/") {
val store by inject<TransientEntityStore>() val store by inject<TransientEntityStore>()
val reporter by inject<StatisticsReporter>()

get { get {
call.respondTemplate("index", mapOf( call.respondTemplate(
"title" to "dogbin" "index", mapOf(
)) "title" to "dogbin"
)
)
} }


get("/{slug}") { get("/{slug}") {
Expand All @@ -42,10 +44,12 @@ fun Route.index() = route("/") {
} else { } else {
if (doc.type == XdDocumentType.URL) { if (doc.type == XdDocumentType.URL) {
runBlocking { runBlocking {
reporter.reportImpression(doc.slug, false, call.request)
reporter.reportEvent(Event.URL_REDIRECT, call.request)
call.respondRedirect(doc.stringContent!!, true) call.respondRedirect(doc.stringContent!!, true)
} }
} else { } else {
documentDto = FrontendDocumentDto.fromDocument(doc, call.locale) documentDto = FrontendDocumentDto.fromDocument(doc, reporter, call.locale)
if (call.session() != null) { if (call.session() != null) {
val usr = call.user(store) val usr = call.user(store)
editable = doc.userCanEdit(usr) editable = doc.userCanEdit(usr)
Expand All @@ -54,6 +58,7 @@ fun Route.index() = route("/") {
} }
} }
if (documentDto != null) { if (documentDto != null) {
reporter.reportImpression(documentDto!!.slug, true, call.request)
call.respondTemplate( call.respondTemplate(
"index", mapOf( "index", mapOf(
"title" to documentDto!!.title, "title" to documentDto!!.title,
Expand All @@ -75,14 +80,15 @@ fun Route.index() = route("/") {
call.respondRedirect("/") call.respondRedirect("/")
} }
} else { } else {
documentDto = FrontendDocumentDto.fromDocument(doc, call.locale) documentDto = FrontendDocumentDto.fromDocument(doc, reporter, call.locale)
if (call.session() != null) { if (call.session() != null) {
val usr = call.user(store) val usr = call.user(store)
editable = doc.userCanEdit(usr) editable = doc.userCanEdit(usr)
} }
} }
} }
if (documentDto != null) { if (documentDto != null) {
reporter.reportImpression(documentDto!!.slug, true, call.request)
call.respondTemplate( call.respondTemplate(
"index", mapOf( "index", mapOf(
"title" to documentDto!!.title, "title" to documentDto!!.title,
Expand All @@ -104,7 +110,7 @@ fun Route.index() = route("/") {
call.respondRedirect("/") call.respondRedirect("/")
} }
} else { } else {
documentDto = FrontendDocumentDto.fromDocument(doc, call.locale) documentDto = FrontendDocumentDto.fromDocument(doc, reporter, call.locale)
if (call.session() != null) { if (call.session() != null) {
val usr = call.user(store) val usr = call.user(store)
canEdit = doc.userCanEdit(usr) canEdit = doc.userCanEdit(usr)
Expand Down

0 comments on commit 7b281ba

Please sign in to comment.