Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whitelabel Tile Server #576

Merged
merged 26 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7a5d47e
Simplify docker setup
maxammann Oct 9, 2022
38d9bac
Add old brainstorming document
maxammann Oct 9, 2022
155fafa
Add style as submodule
maxammann Oct 9, 2022
01d21be
Add an endpoint for serving the map style
maxammann Oct 9, 2022
bee3ef6
Change port to be compatible with macOS
maxammann Oct 11, 2022
dd3f4dd
Fix build for running
maxammann Oct 11, 2022
418ffb5
Dynamically generate style
maxammann Oct 11, 2022
c5b4de7
Merge branch 'whitelabel' into 510-tile-server-whitelabel
maxammann Oct 11, 2022
297ae50
Remove script os
maxammann Oct 11, 2022
11ca2f4
Add format script and run
maxammann Oct 11, 2022
babc808
Fix compilation
maxammann Oct 11, 2022
165cd41
App ApplicationHandler
maxammann Oct 11, 2022
b37b58c
Remove unused source
maxammann Oct 11, 2022
4a7627c
Enable logging for postgres
maxammann Oct 11, 2022
d4dee27
Add example map which can be started through IntelliJ
maxammann Oct 11, 2022
8a3b402
Disable geocoding and fix map url
maxammann Oct 11, 2022
eb74a78
Add projectId and clustered params to martin psql setup
maxammann Oct 11, 2022
2bc834c
Add delete function for stores based on project
maxammann Oct 11, 2022
1f92a18
Use delete function for stores and add test data for nuernberg
maxammann Oct 11, 2022
4ef2332
Fix typo
maxammann Oct 11, 2022
4914cea
Update IntelliJ config
maxammann Oct 11, 2022
8725232
Change database schema to make region optional for accepting stores
maxammann Oct 11, 2022
20d9035
Fix formatting
maxammann Oct 11, 2022
d4ae3ff
Use new map style url in frontend
maxammann Oct 11, 2022
8cec19e
Fix martin packaging
maxammann Oct 11, 2022
7dc4b6f
Return 404 if style is not found
maxammann Oct 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ jobs:
- run:
name: "Build .deb"
command: |
cp ~/project/docker/martin-config.yaml config.yaml
cp /tmp/workspace/martin/martin .
~/project/scripts/pack_deb.sh -v "0.$CIRCLE_BUILD_NUM" -d "Martin tile server for the Ehrenamtskarte app" -n "eak-martin" -s ~/project/scripts/eak-martin.service -C "/opt/ehrenamtskarte/martin/config.yaml" -M .
- run:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "backend/ehrenamtskarte-maplibre-style"]
path = backend/ehrenamtskarte-maplibre-style
url = https://github.com/digitalfabrik/ehrenamtskarte-maplibre-style.git
4 changes: 2 additions & 2 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions .idea/runConfigurations/Format_backend.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion administration/.env.development
Original file line number Diff line number Diff line change
@@ -1 +1 @@
REACT_APP_API_BASE_URL=http://localhost:7000
REACT_APP_API_BASE_URL=http://localhost:8000
maxammann marked this conversation as resolved.
Show resolved Hide resolved
maxammann marked this conversation as resolved.
Show resolved Hide resolved
17 changes: 0 additions & 17 deletions administration/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions administration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
"eslint-config-prettier": "^8.5.0",
"prettier": "^2.7.1",
"protobufjs-cli": "^1.0.2",
"run-script-os": "^1.1.6",
"typescript": "^4.8.3"
},
"scripts": {
Expand All @@ -52,9 +51,7 @@
"lint": "eslint . && prettier . --check",
"format": "prettier . --write",
"generate-graphql": "graphql-codegen --config graphql-codegen.yml",
"generate-protobuf": "run-script-os",
"generate-protobuf:linux": "mkdir -p src/generated && pbjs -t static-module -w es6 -o src/generated/protobuf.js ../specs/card_activate_model.proto --force-long && pbts -o src/generated/protobuf.d.ts src/generated/protobuf.js",
"generate-protobuf:win32": "mkdir src\\generated | pbjs -t static-module -w es6 -o src\\generated\\protobuf.js ..\\specs\\card_activate_model.proto --force-long | pbts -o src\\generated\\protobuf.d.ts src\\generated\\protobuf.js",
"generate-protobuf": "mkdir -p src/generated && pbjs -t static-module -w es6 -o src/generated/protobuf.js ../specs/card_activate_model.proto --force-long && pbts -o src/generated/protobuf.d.ts src/generated/protobuf.js",
"postinstall": "npm run generate-protobuf && npm run generate-graphql"
},
"eslintConfig": {
Expand Down
9 changes: 9 additions & 0 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,12 @@ tasks.withType<JavaExec>().configureEach {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

tasks.register<Copy>("copyStyle") {
from("$rootDir/ehrenamtskarte-maplibre-style/style.json")
into("$buildDir/resources/main/")
}

tasks.named("classes") {
dependsOn(tasks.named("copyStyle"))
}
1 change: 1 addition & 0 deletions backend/ehrenamtskarte-maplibre-style
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package app.ehrenamtskarte.backend.application.webservice

import app.ehrenamtskarte.backend.auth.webservice.JwtService
import io.javalin.http.Context
import java.io.File

class ApplicationHandler(private val applicationData: File) {

fun getPath(): String {
return "/application/{applicationId}/file/{fileIndex}"
}

fun handle(context: Context) {
if (JwtService.verifyRequest(context) !== null) {
val applicationId = context.pathParam("applicationId")
val fileIndex = context.pathParam("fileIndex")
val file = File(this.applicationData, "$applicationId/file/$fileIndex")
if (!file.isFile) {
context.status(404)
} else {
context.contentType("application/octet-stream")
context.result(file.inputStream())
}
} else {
context.status(404)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package app.ehrenamtskarte.backend.common.webservice

import app.ehrenamtskarte.backend.application.webservice.registerApplicationJavalinHandler
import app.ehrenamtskarte.backend.application.webservice.ApplicationHandler
import app.ehrenamtskarte.backend.config.BackendConfiguration
import app.ehrenamtskarte.backend.map.webservice.MapStyleHandler
import io.javalin.Javalin
import io.javalin.http.staticfiles.Location
import java.io.File
Expand Down Expand Up @@ -50,6 +51,8 @@ class WebService {
println("Goto http://$host:$port/graphiql/ for a graphical editor")

val graphQLHandler = GraphQLHandler()
val mapStyleHandler = MapStyleHandler(config)
val applicationHandler = ApplicationHandler(applicationData)

app.post("/") { ctx ->
if (!production) {
Expand All @@ -59,6 +62,16 @@ class WebService {
graphQLHandler.handle(ctx, applicationData)
}

registerApplicationJavalinHandler(app, applicationData)
app.get(mapStyleHandler.getPath()) { ctx ->
if (!production) {
ctx.header("Access-Control-Allow-Headers: Authorization")
ctx.header("Access-Control-Allow-Origin: *")
}
mapStyleHandler.handle(ctx)
}

app.get(applicationHandler.getPath()) { ctx ->
applicationHandler.handle(ctx)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ val possibleBackendConfigurationFiles =
)

data class PostgresConfig(val url: String, val user: String, val password: String)
data class MapConfig(val baseUrl: String)
data class GeocodingConfig(val enabled: Boolean, val host: String)
data class ProjectConfig(val id: String, val importUrl: String, val pipelineName: String)
data class ServerConfig(val dataDirectory: String, val host: String, val port: String)

data class BackendConfiguration(
val production: Boolean,
val server: ServerConfig,
val map: MapConfig,
val postgres: PostgresConfig,
val geocoding: GeocodingConfig,
val projects: List<ProjectConfig>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package app.ehrenamtskarte.backend.map.webservice

import app.ehrenamtskarte.backend.config.BackendConfiguration
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.node.JsonNodeFactory
import com.fasterxml.jackson.databind.node.ObjectNode
import io.javalin.http.Context
import java.io.BufferedReader
import java.io.IOException

class MapStyleHandler(config: BackendConfiguration) {
private var styles: Map<String, String>

init {
val mapper = ObjectMapper()

this.styles = config.projects.associate { project ->
val style = loadStyle() ?: throw Exception("Unable to parse style.json")
val newStyle = patchStyle(style) { id: String, source: JsonNode ->
val newSource = source.deepCopy<ObjectNode>()

val baseUrl = config.map.baseUrl

if (id == "physical_stores") {
val tiles = ArrayNode(JsonNodeFactory.instance)
tiles.add(baseUrl + "?project_id=" + project.id)
newSource.replace("tiles", tiles)
} else if (id == "physical_stores_clustered") {
val tiles = ArrayNode(JsonNodeFactory.instance)
tiles.add(baseUrl + "?clustered=true&project_id=" + project.id)
newSource.replace("tiles", tiles)
}

Pair(id, newSource)
}

val json = mapper.writeValueAsString(newStyle) ?: throw Exception("Failed to create style.json")

Pair(project.id, json)
}
}

private fun patchStyle(
style: JsonNode,
patcher: (id: String, source: JsonNode) -> Pair<String, JsonNode>
): JsonNode? {
val sources = style["sources"] ?: return null

val newSources = sources.fields().asSequence().map {
patcher(it.key, it.value)
}.toMap()

val newSourcesObject = ObjectNode(JsonNodeFactory.instance, newSources)

val newStyle = style.deepCopy<ObjectNode>() ?: return null

newStyle.replace("sources", newSourcesObject)

return newStyle
}

private fun loadStyle(): JsonNode? {
val mapper = ObjectMapper()
val resource =
ClassLoader.getSystemResource("style.json") ?: throw Error("Map style 'style.json' is missing missing!'")
maxammann marked this conversation as resolved.
Show resolved Hide resolved

val reader = BufferedReader(resource.openStream().reader(Charsets.UTF_8))
val text = reader.readText()
return mapper.readTree(text)
}

fun getPath(): String {
return "/project/{project_id}/map"
}

fun handle(context: Context) {
try {
val projectId: String = context.pathParam("project_id")
val style: String = this.styles[projectId]!!
context.result(style)
context.contentType("application/json")
} catch (e: IOException) {
context.res().sendError(500)
} catch (e: Throwable) {
println(e)
maxammann marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package app.ehrenamtskarte.backend.projects.database

import app.ehrenamtskarte.backend.stores.database.AcceptingStores
import app.ehrenamtskarte.backend.stores.database.Addresses
import app.ehrenamtskarte.backend.stores.database.Contacts
import app.ehrenamtskarte.backend.stores.database.PhysicalStores
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.select

object Projects : IntIdTable() {
val project = varchar("project", 50).uniqueIndex()
Expand All @@ -13,4 +21,43 @@ class ProjectEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<ProjectEntity>(Projects)

var project by Projects.project

fun deleteAssociatedStores() {
val project = this

val acceptingStoresDelete =
AcceptingStores.slice(AcceptingStores.id).select { AcceptingStores.projectId eq project.id }
.map { it[AcceptingStores.id] }

val contactsDelete =
(AcceptingStores innerJoin Contacts).slice(Contacts.id)
.select { AcceptingStores.projectId eq project.id }
.map { it[Contacts.id] }

val physicalStoresDelete =
(PhysicalStores innerJoin AcceptingStores).slice(PhysicalStores.id)
.select { AcceptingStores.projectId eq project.id }
.map { it[PhysicalStores.id] }

val addressesDelete =
((PhysicalStores innerJoin Addresses) innerJoin AcceptingStores).slice(Addresses.id)
.select { AcceptingStores.projectId eq project.id }
.map { it[Addresses.id] }

PhysicalStores.deleteWhere {
id inList physicalStoresDelete
}

Addresses.deleteWhere {
id inList addressesDelete
}

AcceptingStores.deleteWhere {
id inList acceptingStoresDelete
}

Contacts.deleteWhere {
id inList contactsDelete
}
}
}
Loading