A lot done here: Bump up to Micronaut 3.7.2 + first Zipkin integration + improved SWS integration + improved SSH service + etc.

Signed-off-by: Thraax Session <thraax.session@iatlas.technology>
This commit is contained in:
Thraax Session 2022-10-22 16:32:45 +02:00
parent cb9201170f
commit 0ce749634e
19 changed files with 1924 additions and 173 deletions

22
.fleet/run.json Normal file
View File

@ -0,0 +1,22 @@
{
"configurations": [
{
"type": "gradle",
"name": "Start Dev/Secured",
"environment": {
"MICRONAUT_ENVIRONMENTS": "dev"
},
"args": [
"-Pmicronaut.server.cors.enabled='false'",
"-Pmicronaut.security.enabled=true",
"-Pmicronaut.server.host=0.0.0.0",
"-Pspaceup.logging.level=DEBUG",
"-Pspaceup.scheduler.domains.update=10m",
"-Dmongodb.uri=mongodb://thraax_mongoroot:peijao3poongoof2sheewepahChoo3ic@mongo.iatlas.technology:41421/admin"
],
"tasks": [
"run"
]
}
]
}

3
.fleet/settings.json Normal file
View File

@ -0,0 +1,3 @@
{
"toolchains": []
}

View File

@ -32,7 +32,7 @@ The server application is written in kotlin with micronaut as web framework. The
If any other feature is wished, or you want to *help me*, write me a small email.
So I will register you for youtrack to open an issue, or we have a discussion how you can support the project:
<spaceup-support@iatlas.technology>
spaceup-support@iatlas.technology
Little big bonus: There is a multiplatform app (written in Flutter) which already consumes the APIs. 😁
You should know it's not tested with ios/osx as I am not an apple user.

View File

@ -27,15 +27,15 @@ configurations {
dependencies {
kapt(platform("io.micronaut:micronaut-bom:$micronautVersion"))
kapt('io.micronaut:micronaut-inject-java:3.6.3')
kapt('io.micronaut:micronaut-validation:3.6.3')
kapt('io.micronaut:micronaut-inject-java:3.7.1')
kapt('io.micronaut:micronaut-validation:3.7.1')
kapt("io.micronaut:micronaut-graal:$micronautVersion")
kapt('io.micronaut.openapi:micronaut-openapi:4.5.1')
kapt('io.micronaut.openapi:micronaut-openapi:4.5.2')
kapt('io.micronaut.security:micronaut-security-annotations:3.8.0')
compileOnly(platform("io.micronaut:micronaut-bom:$micronautVersion"))
compileOnly('org.graalvm.nativeimage:svm:22.2.0')
implementation(platform("io.micronaut:micronaut-bom:$micronautVersion"))
implementation('io.micronaut:micronaut-management:3.6.3')
implementation('io.micronaut:micronaut-management:3.7.1')
implementation("io.micronaut:micronaut-inject:$micronautVersion")
implementation("io.micronaut:micronaut-validation:$micronautVersion")
implementation('io.micronaut.rxjava3:micronaut-rxjava3:2.3.0')
@ -48,9 +48,11 @@ dependencies {
implementation("io.micronaut:micronaut-http-server-netty:$micronautVersion")
implementation("io.micronaut:micronaut-http-client:$micronautVersion")
implementation("io.micronaut.reactor:micronaut-reactor")
implementation('io.swagger.core.v3:swagger-annotations:2.2.2')
implementation('io.micronaut:micronaut-tracing:3.2.7')
runtimeOnly('io.jaegertracing:jaeger-thrift:1.8.1')
implementation('io.swagger.core.v3:swagger-annotations:2.2.4')
implementation('io.micronaut:micronaut-tracing')
implementation("io.opentracing.brave:brave-opentracing:1.0.0")
runtimeOnly("io.zipkin.brave:brave-instrumentation-httpclient:5.14.1")
runtimeOnly("io.zipkin.reporter:zipkin-reporter:1.1.2")
implementation("io.micronaut.security:micronaut-security:3.2.0")
implementation('io.micronaut.security:micronaut-security-jwt:3.8.0')
implementation('io.micronaut.kotlin:micronaut-kotlin-extension-functions:3.2.2')
@ -72,7 +74,7 @@ dependencies {
implementation('org.jasypt:jasypt:1.9.3')
// My stuff
implementation "technology.iatlas.sws:sws:1.3.2-SNAPSHOT"
implementation("technology.iatlas.sws:sws:1.4.0-SNAPSHOT")
runtimeOnly("ch.qos.logback:logback-classic")
runtimeOnly('com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1')
@ -86,7 +88,7 @@ dependencies {
testImplementation("io.micronaut.test:micronaut-test-junit5:$micronautVersion")
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.9.0')
// Docker Testcontainer
testImplementation 'org.testcontainers:testcontainers:1.17.3'
testImplementation 'org.testcontainers:testcontainers:1.17.4'
}
test.classpath += configurations.developmentOnly
@ -123,10 +125,10 @@ kapt {
arg("micronaut.processing.incremental", true)
arg("micronaut.processing.annotations", "technology.iatlas.spaceup.*")
arg("micronaut.processing.group", "technology.iatlas.spaceup")
arg("micronaut.processing.module", "spaceUp")
arg("micronaut.processing.module", "SpaceUp")
arg("micronaut.openapi.views.spec",
"redoc.enabled=true,rapidoc.enabled=true," +
"swagger-ui.enabled=true,swagger-ui.theme=flattop")
"swagger-ui.enabled=true,swagger-ui.theme=material")
}
}

View File

@ -40,6 +40,6 @@
# Thanks, and we hope you enjoy using SpaceUp-Server and that it's everything you ever hoped it could be.
#
micronautVersion=3.7.1
micronautVersion=3.7.2
kotlinVersion=1.7.20
kotlin.code.style=official

View File

@ -71,14 +71,6 @@ import java.text.Normalizer.Form
import java.text.Normalizer as jNormalizer
@OpenAPIDefinition(
info = Info(
title = "SpaceUp",
version = "0.26.0",
contact = Contact(name = "Thraax Session",
url = "https://spaceup.iatlas.technology", email = "spaceup@iatlas.technology")
)
)
@TypeHint(
value = [
LoggerFactory::class,
@ -120,6 +112,14 @@ import java.text.Normalizer as jNormalizer
"com.jcraft.jsch.UserAuthKeyboardInteractive",
]
)
@OpenAPIDefinition(
info = Info(
title = "SpaceUp",
version = "0.26.0",
contact = Contact(name = "Thraax Session",
url = "https://spaceup.iatlas.technology", email = "spaceup@iatlas.technology")
)
)
object Api
fun main(args: Array<String>) {

View File

@ -47,6 +47,7 @@ import io.micronaut.http.MediaType
import io.micronaut.http.annotation.*
import io.micronaut.security.annotation.Secured
import io.micronaut.security.rules.SecurityRule
import io.micronaut.tracing.annotation.ContinueSpan
import org.slf4j.LoggerFactory
import technology.iatlas.spaceup.core.annotations.Installed
import technology.iatlas.spaceup.dto.Feedback
@ -73,6 +74,7 @@ class SwsController(
}
@Get(uri = "/all", produces = [MediaType.APPLICATION_JSON])
@ContinueSpan
suspend fun getAllSws(): HttpResponse<List<Sws>> {
log.info("Retrieve all sws configurations")
val allSws = swsService.getAll()

View File

@ -42,6 +42,7 @@
package technology.iatlas.spaceup.core.httpfilter
import brave.Tracing
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpStatus
import io.micronaut.http.MutableHttpResponse
@ -51,6 +52,7 @@ import io.micronaut.http.filter.ServerFilterChain
import io.micronaut.http.simple.SimpleHttpResponseFactory
import io.micronaut.security.annotation.Secured
import io.micronaut.security.rules.SecurityRule
import io.micronaut.tracing.annotation.NewSpan
import kotlinx.coroutines.runBlocking
import org.reactivestreams.Publisher
import reactor.core.publisher.Flux
@ -61,21 +63,25 @@ import technology.iatlas.spaceup.services.SpaceUpService
import technology.iatlas.spaceup.services.SwsService
@Filter("/api/sws/custom/**")
@Filter("/api/sws/execution/**")
@Secured(SecurityRule.IS_AUTHENTICATED)
class SwsRouteFilter(
open class SwsRouteFilter(
private val swsService: SwsService,
private val dbService: DbService,
private val spaceUpService: SpaceUpService
private val spaceUpService: SpaceUpService,
private val tracing: Tracing
): HttpServerFilter {
override fun doFilter(request: HttpRequest<*>, chain: ServerFilterChain?): Publisher<MutableHttpResponse<*>> {
@NewSpan("sws-http")
override fun doFilter(
request: HttpRequest<*>, chain: ServerFilterChain?): Publisher<MutableHttpResponse<*>> {
return Flux.from(Mono.fromCallable {
runBlocking {
if(dbService.isAppInstalled() && (request.userPrincipal.isPresent || spaceUpService.isDevMode())) {
if(dbService.isAppInstalled() &&
((request.userPrincipal.isPresent && !spaceUpService.isDevMode()) || spaceUpService.isDevMode())) {
swsService.execute(request)
} else {
SimpleHttpResponseFactory.INSTANCE.status(
SimpleHttpResponseFactory.INSTANCE.status<String>(
HttpStatus.INTERNAL_SERVER_ERROR, "Server is not installed or user is not logged in.")
}
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2022 spaceup@iatlas.technology.
* Copyright (c) 2022 Thraax Session <spaceup@iatlas.technology>.
*
* SpaceUp-Server is free software; You can redistribute it and/or modify it under the terms of:
* - the GNU Affero General Public License version 3 as published by the Free Software Foundation.
* You don't have to do anything special to accept the license and you dont have to notify anyone which that you have made that decision.
@ -11,7 +12,6 @@
* You should have received a copy of both licenses along with SpaceUp-Server
* If not, see <http://www.gnu.org/licenses/>.
*
*
* There is a strong belief within us that the license we have chosen provides not only the best solution for providing you with the essential freedom necessary to use SpaceUp-Server within your projects, but also for maintaining enough copyleft strength for us to feel confident and secure with releasing our hard work to the public. For your convenience we've included our own interpretation of the license we chose, which can be seen below.
*
* Our interpretation of the GNU Affero General Public License version 3: (Quoted words are words in which there exists a definition within the license to avoid ambiguity.)
@ -62,8 +62,8 @@ class InstalledInterceptor(
val db = dbService.getDb()
val serverRepo = db.getCollection<Server>()
val server = serverRepo.find().first()!!
if(!server.installed) {
val server: Server? = serverRepo.find().first()
if(server == null || !server.installed) {
throw NotInstalledException("Application needs to be proper installed!")
}

View File

@ -60,6 +60,7 @@ import technology.iatlas.spaceup.services.InstallerService
import technology.iatlas.spaceup.services.SpaceUpService
import technology.iatlas.spaceup.services.SshService
import technology.iatlas.spaceup.services.SwsService
import java.io.File
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.createDirectories
@ -92,18 +93,20 @@ class StartupEventListener(
}
}
// Step 1: create directories if not exist
createDirectories()
runBlocking {
createExternalDirectories()
// Step 1: check if spaceup was installed
val isInstalled = checkInstallation()
if(isInstalled) {
// Step 2: create directories if not exist
createDirectories()
runBlocking {
createExternalDirectories()
}
// Step 3: fill sws cache
fillSwsCache()
}
// Step 2: check if spaceup was installed
checkInstallation()
// Step 3: fill sws cache
fillSwsCache()
log.info("Finished SpaceUp startup")
}
@ -150,12 +153,13 @@ class StartupEventListener(
}
}
private fun checkInstallation() {
private fun checkInstallation(): Boolean {
// Let's check if we are already installed properly
val db = dbService.getDb()
val serverRepo = db.getCollection<Server>()
val server = serverRepo.find().firstOrNull()
var isInstalled = false
if(server == null) {
log.info("Seems to be first run. Set not installed!")
val apiKey = installerService.generateAPIKey()
@ -171,7 +175,10 @@ class StartupEventListener(
log.info("Finish installation with API key: ${server.apiKey.yellow.bold}")
}
}
isInstalled = installed
}
return isInstalled
}
private fun fillSwsCache() {
@ -198,4 +205,8 @@ class StartupEventListener(
fun String.createNormalizedPath(): Path {
return Path(this).normalize()
}
fun String.toFile(): File {
return this.createNormalizedPath().toFile()
}

View File

@ -50,9 +50,9 @@ import com.jcraft.jsch.JSchException
import com.jcraft.jsch.Session
import io.micronaut.context.annotation.Context
import io.micronaut.context.annotation.Value
import kotlinx.coroutines.Dispatchers
import io.micronaut.tracing.annotation.NewSpan
import io.micronaut.tracing.annotation.SpanTag
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.litote.kmongo.findOne
import org.litote.kmongo.getCollection
import org.slf4j.LoggerFactory
@ -83,7 +83,7 @@ class SshService(
private lateinit var useDbCredentials: String
// Configure SSH
fun initSSH() {
suspend fun initSSH() {
val jsch = JSch()
var username = ""
@ -146,13 +146,14 @@ class SshService(
}
}
suspend fun execute(command: CommandInf): SshResponse {
@NewSpan
suspend fun execute(@SpanTag("ssh-command")command: CommandInf): SshResponse {
log.trace("Execute $command")
if(!this::session.isInitialized || !session.isConnected) {
initSSH()
}
val channel: ChannelExec = try {
val executionChannel: ChannelExec = try {
session.openChannel("exec") as ChannelExec
} catch (shhEx: JSchException) {
log.error("SSH Session is down. Will try to reconnect.")
@ -161,31 +162,33 @@ class SshService(
}
try {
channel.setCommand(command.parameters.joinToString(" "))
executionChannel.setCommand(command.parameters.joinToString(" "))
val responseStream = ByteArrayOutputStream()
val errorResponseStream = ByteArrayOutputStream()
channel.outputStream = responseStream
channel.setErrStream(errorResponseStream)
executionChannel.outputStream = responseStream
executionChannel.setErrStream(errorResponseStream)
try {
channel.connect()
executionChannel.connect()
} catch (shhEx: JSchException) {
log.error("SSH Session is down. Will try to reconnect.")
initSSH()
}
// When then channel close itself, we retrieved the data
while (channel.isConnected) {
delay(50)
while (executionChannel.isConnected) {
delay(20)
}
val stdout = String(responseStream.toByteArray())
var stderr = String(errorResponseStream.toByteArray())
if(stderr.isNotEmpty() && channel.exitStatus != 0) {
if (stderr.isNotEmpty() && executionChannel.exitStatus != 0) {
stderr = """
Command: ${command.parameters.joinToString { " " }}
terminated with exit code: ${channel.exitStatus}
""".trimIndent()
Script: ${command.shellScript}
Command: ${command.parameters.joinToString(" ")}
Script error: $stderr
terminated with exit code: ${executionChannel.exitStatus}
""".trimIndent()
colored {
log.error(stderr.red)
}
@ -196,7 +199,7 @@ class SshService(
return sshResponse
} finally {
//session.disconnect()
channel.disconnect()
executionChannel.disconnect()
}
}
@ -204,7 +207,8 @@ class SshService(
* Upload a shell script via SFTP and execute it optionally
*
*/
suspend fun upload(cmd: CommandInf): SshResponse {
@NewSpan
suspend fun upload(@SpanTag("ssh-command") cmd: CommandInf): SshResponse {
log.debug("Upload $cmd")
if(!session.isConnected) {
initSSH()
@ -219,67 +223,31 @@ class SshService(
sftpConfig.remotedir?.replace("~", "/home/${ssh.username}") + "/${file.name}"
val sftpChannel: Channel = session.openChannel("sftp") as Channel
val executionChannel: ChannelExec = session.openChannel("exec") as ChannelExec
val executeCmd = cmd.parameters.joinToString(" ")
executionChannel.setCommand(executeCmd)
val responseExecution = ByteArrayOutputStream()
val errorExecution = ByteArrayOutputStream()
executionChannel.outputStream = responseExecution
executionChannel.setErrStream(errorExecution)
var sshResponse = SshResponse("", "")
try {
sftpChannel.connect()
if(!sftpChannel.isConnected) {
sftpChannel.connect()
}
val sftp = sftpChannel as ChannelSftp
log.debug("Upload script ${file.name} to $remotefile")
withContext(Dispatchers.IO) {
val os = System.getProperty("os.name")
if(os.lowercase().contains("windows")) {
val localScript = file.scriptPath?.file ?: ""
sftp.put(File(localScript).canonicalPath, remotefile, ChannelSftp.OVERWRITE)
} else {
sftp.put(file.scriptPath?.openStream(), remotefile, ChannelSftp.OVERWRITE)
}
val os = System.getProperty("os.name")
if(os.lowercase().contains("windows")) {
val localScript = file.scriptPath?.file ?: ""
sftp.put(File(localScript).canonicalPath, remotefile, ChannelSftp.OVERWRITE)
} else {
sftp.put(file.scriptPath?.file, remotefile, ChannelSftp.OVERWRITE)
}
if(file.execute) {
log.trace("""
"Execute script: ${cmd.parameters}
$executeCmd
""".trimIndent())
executionChannel.connect()
// When then channel close itself, we retrieved the data
while (executionChannel.isConnected) {
delay(50)
}
val stdout = String(responseExecution.toByteArray())
var stderr = String(errorExecution.toByteArray())
if(stderr.isNotEmpty() && executionChannel.exitStatus != 0) {
stderr = """
Script: ${cmd.shellScript}
Command: ${cmd.parameters.joinToString(" ")}
Script error: $stderr
terminated with exit code: ${executionChannel.exitStatus}
""".trimIndent()
colored {
log.error(stderr.red)
}
}
sshResponse = SshResponse(stdout, stderr)
// SshResponse
log.trace(sshResponse.toString())
sshResponse = execute(cmd)
}
log.trace(sshResponse.toString())
return sshResponse
} finally {
sftpChannel.disconnect()
executionChannel.disconnect()
}
}
}

View File

@ -43,11 +43,13 @@
package technology.iatlas.spaceup.services
import com.mongodb.client.MongoCollection
import io.micronaut.context.annotation.Context
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpStatus
import io.micronaut.http.MutableHttpResponse
import io.micronaut.http.simple.SimpleHttpResponseFactory
import io.micronaut.tracing.annotation.NewSpan
import io.micronaut.tracing.annotation.SpanTag
import jakarta.inject.Singleton
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.litote.kmongo.eq
@ -56,7 +58,7 @@ import technology.iatlas.spaceup.config.SpaceUpSftpConfig
import technology.iatlas.spaceup.config.SpaceupPathConfig
import technology.iatlas.spaceup.core.cmd.SshResponse
import technology.iatlas.spaceup.core.cmd.toFeedback
import technology.iatlas.spaceup.core.startup.createNormalizedPath
import technology.iatlas.spaceup.core.startup.toFile
import technology.iatlas.spaceup.dto.Command
import technology.iatlas.spaceup.dto.Feedback
import technology.iatlas.spaceup.dto.SftpFile
@ -66,8 +68,8 @@ import technology.iatlas.sws.SWS
import technology.iatlas.sws.objects.ParserException
import java.io.File
@Context
class SwsService(
@Singleton
open class SwsService(
private val dbService: DbService,
private val sshService: SshService,
private val sftpConfig: SpaceUpSftpConfig,
@ -81,7 +83,7 @@ class SwsService(
private fun validateSWS(sws: Sws, feedback: Feedback) {
log.info("Validate sws")
// Create a temporary file
"$tempDir/${sws.name}.sws".createNormalizedPath().toFile().apply {
"$tempDir/${sws.name}.sws".toFile().apply {
this.writeText(sws.content)
try {
SWS.createAndParse(this)
@ -198,11 +200,12 @@ class SwsService(
}
}
suspend fun execute(request: HttpRequest<*>): MutableHttpResponse<Feedback> {
val path = request.path.split("/api/sws/custom")[1]
@NewSpan("sws-execute")
open suspend fun execute(@SpanTag("http-request") request: HttpRequest<*>): MutableHttpResponse<Feedback> {
val path = request.path.split("/api/sws/execution")[1]
val httpMethod = request.method
val parameters: Map<String, List<String>> = request.parameters.asMap()
val bodyOptional = request.body
val httpBody = request.body
log.info("Execute SWS [$httpMethod] [$path] ${parameters.map {
if(it.key.lowercase() == "pass" || it.key.lowercase() == "password")
@ -212,25 +215,25 @@ class SwsService(
// Handle HTTP request parameters
val swsHttpParameters = mutableMapOf<String, Any?>()
parameters.forEach { (t, u) ->
parameters.forEach { (k, v) ->
try {
// Values come always as String. We have to cast numbers to integers
swsHttpParameters[t] = Integer.valueOf(u[0])
swsHttpParameters[k] = Integer.valueOf(v[0])
} catch (ex: NumberFormatException) {
swsHttpParameters[t] = u[0]
swsHttpParameters[k] = v[0]
}
}
// TODO: SU-19 Handle request body, important to transport secrets/credentials
val body = mutableMapOf<String, Any?>()
if(bodyOptional.isPresent) {
if(httpBody.isPresent) {
// The body is a map of anything
val tempBody = bodyOptional.get() as Map<*, *>
val tempBody = httpBody.get() as Map<*, *>
tempBody.forEach { (k, v) ->
if(k is String) {
if(k is String) { // the key needs always be a string for mapping
body[k] = v
} else {
log.warn("Key $k is not a string. Will be ignored!")
log.warn("Key $k is not a string. '$k' and '$v' will be ignored!")
}
}
}
@ -254,7 +257,7 @@ class SwsService(
// Generate SWS
var sws: SWS
val file = "$tempDir/${swsDb.name}.sws".createNormalizedPath().toFile()
val file = "$tempDir/${swsDb.name}.sws".toFile()
file.bufferedWriter().use {
it.write(swsDb.content)
}.apply {
@ -272,7 +275,7 @@ class SwsService(
// Actual execution
var response: SshResponse
val scriptname = "${sws.name.replace(" ", "_")}.sh"
"$tempDir/$scriptname".createNormalizedPath().toFile().apply {
"$tempDir/$scriptname".toFile().apply {
this.writeText(sws.serverScript)
val script = "${sftpConfig.remotedir}/$scriptname"

View File

@ -83,20 +83,33 @@ micronaut.server.ssl.build-self-signed=true
micronaut.server.http-version=1.1
micronaut.server.ssl.port=9094
# Tracing
tracing.zipkin.enabled=false
tracing.zipkin.http.url=http://localhost:9411
tracing.zipkin.sampler.probability=1
micronaut.caches.my-cache.maximumSize=100
# Hotreload for views
micronaut.views.rocker.hot-reloading=true
micronaut.openapi.property.naming.strategy=KEBAB_CASE
micronaut.openapi.target.file=myspecfile.yml
micronaut.openapi.target.file=spaceup-openapi.yml
swagger.enabled=true
swagger-ui.enabled=true
redoc.enabled=true
# Defaults
#micronaut.router.static-resources.default.mapping=/**
#micronaut.router.static-resources.default.enabled=true
#micronaut.router.static-resources.default.paths=classpath:static
spaceup.home=${user.home}/.spaceup
# Web client
# Will be used to download the client there
spaceup.ui.dir=${spaceup.home}/static/ui
spaceup.ui.enabled=false
micronaut.router.static-resources.default.mapping=/ui/**
micronaut.router.static-resources.default.enabled=${spaceup.ui.enabled}
micronaut.router.static-resources.default.paths=file:${spaceup.ui.dir}
# Swagger
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
@ -122,6 +135,7 @@ devurl.Redoc.enabled=true
devurl.Redoc.mapping=/redoc
devurl.Redoc.newtab=true
# Update this to spaceup.remote.path...
spaceup.path.services=~/etc/services.d/
spaceup.path.logs=~/logs/
spaceup.path.temp=~/.spaceup/tmp
@ -133,6 +147,7 @@ spaceup.path.temp=~/.spaceup/tmp
#spaceup.ssh.password
#spaceup.ssh.host
spaceup.ssh.port=22
# Update this to spaceup.sftp.remote.temp
spaceup.sftp.remotedir=~/.spaceup/tmp
# If set, SpaceUp will take the credentials from db.
# # Otherwise you have to supply -spaceup.ssh.* configuration.

View File

@ -66,7 +66,7 @@
rapidoc.requestUpdate();
});
}
rapidoc.setAttribute('spec-url', contextPath + '/swagger/myspecfile.yml');
rapidoc.setAttribute('spec-url', contextPath + '/swagger/spaceup-openapi.yml');
</script>
</body>

View File

@ -65,7 +65,7 @@
},
cookie = extract(document.cookie),
contextPath = cookie === '' ? extract(window.location.search.substring(1)) : cookie;
Redoc.init(contextPath + '/swagger/myspecfile.yml');
Redoc.init(contextPath + '/swagger/spaceup-openapi.yml');
</script>
</body>

View File

@ -51,7 +51,7 @@
<script src='res/swagger-ui-bundle.js'></script>
<script src='res/swagger-ui-standalone-preset.js'></script>
<link rel='stylesheet' type='text/css' href='res/swagger-ui.css' />
<link rel='stylesheet' type='text/css' href='res/flattop.css' />
<link rel='stylesheet' type='text/css' href='res/material.css' />
</head>
@ -85,7 +85,7 @@
};
},
ui = SwaggerUIBundle({
url: contextPath + '/swagger/myspecfile.yml',
url: contextPath + '/swagger/spaceup-openapi.yml',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long