From 329521365ad8835b8a4fe12a3a3eb9e5dd51e84a Mon Sep 17 00:00:00 2001 From: Quentin Aubert Date: Tue, 7 Nov 2023 14:08:02 +0100 Subject: [PATCH 01/11] FIX database migration issue due to non evulated subscritionDemand notification & consumptions with no state --- .../app/controllers/AdminApiController.scala | 1 + daikoku/app/env/evolutions.scala | 48 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/daikoku/app/controllers/AdminApiController.scala b/daikoku/app/controllers/AdminApiController.scala index 21d5b8233..d45e858db 100644 --- a/daikoku/app/controllers/AdminApiController.scala +++ b/daikoku/app/controllers/AdminApiController.scala @@ -95,6 +95,7 @@ class StateController(DaikokuAction: DaikokuAction, tenant.copy(tenantMode = TenantMode.Maintenance.some))) } _ <- removeAllUserSessions(ctx) + _ <- env.dataStore.notificationRepo.forAllTenant().delete(Json.obj("status.status" -> Json.obj("$ne" -> "Pending"))) _ <- postgresStore.checkDatabase() source = env.dataStore.exportAsStream(pretty = false) _ <- postgresStore.importFromStream(source) diff --git a/daikoku/app/env/evolutions.scala b/daikoku/app/env/evolutions.scala index e12054425..47e56d906 100644 --- a/daikoku/app/env/evolutions.scala +++ b/daikoku/app/env/evolutions.scala @@ -893,6 +893,51 @@ object evolution_1630 extends EvolutionScript { } } + +object evolution_1634 extends EvolutionScript { + override def version: String = "16.3.4" + + override def script: ( + Option[DatastoreId], + DataStore, + Materializer, + ExecutionContext, + OtoroshiClient + ) => Future[Done] = + ( + _: Option[DatastoreId], + dataStore: DataStore, + mat: Materializer, + ec: ExecutionContext, + _: OtoroshiClient + ) => { + AppLogger.info(s"Begin evolution $version - update all consumptions to set state") + + implicit val executionContext: ExecutionContext = ec + implicit val materializer: Materializer = mat + + dataStore.consumptionRepo.forAllTenant().streamAllRaw() + .mapAsync(10)(consumption => { + (consumption \ "state").asOpt(json.ApiKeyConsumptionStateFormat) match { + case Some(_) => FastFuture.successful(()) + case None => + val from = (consumption \ "from").as(json.DateTimeFormat) + val to = (consumption \ "to").as(json.DateTimeFormat) + val id = (consumption \ "_id").as[String] + + val state = if (from.plusDays(1).equals(to)) + ApiKeyConsumptionState.Completed + else + ApiKeyConsumptionState.InProgress + + dataStore.consumptionRepo.forAllTenant() + .save(Json.obj("_id" -> id), consumption.as[JsObject] + ("state" -> json.ApiKeyConsumptionStateFormat.writes(state))) + } + }) + .runWith(Sink.ignore) + } +} + object evolutions { val list: List[EvolutionScript] = List( @@ -908,7 +953,8 @@ object evolutions { evolution_1612_c, evolution_1613, evolution_1613_b, - evolution_1630 + evolution_1630, + evolution_1634 ) def run( dataStore: DataStore, From 581749dffe6c429a8baa43b01ebbed1a5d79309a Mon Sep 17 00:00:00 2001 From: Quentin Aubert Date: Tue, 7 Nov 2023 14:33:38 +0100 Subject: [PATCH 02/11] remove logger --- daikoku/app/domain/json.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daikoku/app/domain/json.scala b/daikoku/app/domain/json.scala index 2df3549d1..124dfd88d 100644 --- a/daikoku/app/domain/json.scala +++ b/daikoku/app/domain/json.scala @@ -1673,7 +1673,7 @@ object json { ) } recover { case e => - AppLogger.error(e.getMessage, e) + //AppLogger.error(e.getMessage, e) JsError(e.getMessage) } get } From 19232a4ddb76985dc4bf18a975b7e5985b4d34ed Mon Sep 17 00:00:00 2001 From: Quentin Aubert Date: Tue, 7 Nov 2023 15:51:55 +0100 Subject: [PATCH 03/11] Upgrade play & disable http2 --- .github/workflows/test.yml | 2 +- daikoku/conf/base.conf | 2 ++ daikoku/project/plugins.sbt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d6175e132..9e515ea0d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: distribution: 'temurin' java-version: '11' #test DK - - uses: coursier/cache-action@v5 + - uses: coursier/cache-action@v6 - name: test id: test run: | diff --git a/daikoku/conf/base.conf b/daikoku/conf/base.conf index c6fd19603..36ae0c98a 100644 --- a/daikoku/conf/base.conf +++ b/daikoku/conf/base.conf @@ -157,6 +157,8 @@ http.port = 8080 http.port = ${?PORT} https.port = disabled https.port = ${?HTTPS_PORT} +http2.enabled = no +http2.enabled = ${?HTTP2_ENABLED} play { application.loader = "fr.maif.otoroshi.daikoku.DaikokuLoader" diff --git a/daikoku/project/plugins.sbt b/daikoku/project/plugins.sbt index ef1fd9318..d19118df5 100644 --- a/daikoku/project/plugins.sbt +++ b/daikoku/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.13") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.19") addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.3") addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.12") From 18fa79df20075b5d101d0ac354317cf2470a9779 Mon Sep 17 00:00:00 2001 From: Quentin Aubert Date: Tue, 7 Nov 2023 15:53:38 +0100 Subject: [PATCH 04/11] ... --- daikoku/test/daikoku/EnvironmentDisplayMode.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daikoku/test/daikoku/EnvironmentDisplayMode.scala b/daikoku/test/daikoku/EnvironmentDisplayMode.scala index ec4f1ff7f..9692af0f4 100644 --- a/daikoku/test/daikoku/EnvironmentDisplayMode.scala +++ b/daikoku/test/daikoku/EnvironmentDisplayMode.scala @@ -41,7 +41,7 @@ class EnvironmentDisplayMode() autoRotation = Some(false) ) - "a usage plan" must { + "a usage plan" must { "have a custom name as an avalaible environment" in { val api = generateApi("0", tenant.id, teamOwnerId, Seq.empty).api From 14233148bedab1367b225cdde9d73e81aaec934f Mon Sep 17 00:00:00 2001 From: daikoku-github-actions Date: Tue, 7 Nov 2023 15:19:48 +0000 Subject: [PATCH 05/11] Format sources before release --- .../app/controllers/AdminApiController.scala | 5 +- daikoku/app/domain/json.scala | 8 +- daikoku/app/utils/otoroshi.scala | 106 +++++++++++------- daikoku/javascript/src/services/index.ts | 21 ++-- daikoku/javascript/src/types/api.ts | 2 +- 5 files changed, 89 insertions(+), 53 deletions(-) diff --git a/daikoku/app/controllers/AdminApiController.scala b/daikoku/app/controllers/AdminApiController.scala index d45e858db..11d63eed2 100644 --- a/daikoku/app/controllers/AdminApiController.scala +++ b/daikoku/app/controllers/AdminApiController.scala @@ -95,7 +95,10 @@ class StateController(DaikokuAction: DaikokuAction, tenant.copy(tenantMode = TenantMode.Maintenance.some))) } _ <- removeAllUserSessions(ctx) - _ <- env.dataStore.notificationRepo.forAllTenant().delete(Json.obj("status.status" -> Json.obj("$ne" -> "Pending"))) + _ <- env.dataStore.notificationRepo + .forAllTenant() + .delete( + Json.obj("status.status" -> Json.obj("$ne" -> "Pending"))) _ <- postgresStore.checkDatabase() source = env.dataStore.exportAsStream(pretty = false) _ <- postgresStore.importFromStream(source) diff --git a/daikoku/app/domain/json.scala b/daikoku/app/domain/json.scala index 124dfd88d..a8ca9d9e7 100644 --- a/daikoku/app/domain/json.scala +++ b/daikoku/app/domain/json.scala @@ -109,7 +109,8 @@ object json { clientSecret = (json \ "clientSecret") .asOpt[String] .getOrElse("admin-api-apikey-secret"), - elasticConfig = (json \ "elasticConfig").asOpt(ElasticAnalyticsConfig.format) + elasticConfig = + (json \ "elasticConfig").asOpt(ElasticAnalyticsConfig.format) ) ) } recover { @@ -122,7 +123,10 @@ object json { "host" -> o.host, "clientId" -> o.clientId, "clientSecret" -> o.clientSecret, - "elasticConfig" -> o.elasticConfig.map(ElasticAnalyticsConfig.format.writes).getOrElse(JsNull).as[JsValue] + "elasticConfig" -> o.elasticConfig + .map(ElasticAnalyticsConfig.format.writes) + .getOrElse(JsNull) + .as[JsValue] ) } diff --git a/daikoku/app/utils/otoroshi.scala b/daikoku/app/utils/otoroshi.scala index 32e24f055..a1de670bb 100644 --- a/daikoku/app/utils/otoroshi.scala +++ b/daikoku/app/utils/otoroshi.scala @@ -6,10 +6,19 @@ import cats.data.EitherT import cats.implicits.catsSyntaxOptionId import controllers.AppError import controllers.AppError.OtoroshiError -import fr.maif.otoroshi.daikoku.audit.{ElasticReadsAnalytics, ElasticWritesAnalytics} +import fr.maif.otoroshi.daikoku.audit.{ + ElasticReadsAnalytics, + ElasticWritesAnalytics +} import fr.maif.otoroshi.daikoku.audit.config.ElasticAnalyticsConfig import fr.maif.otoroshi.daikoku.domain.json.ActualOtoroshiApiKeyFormat -import fr.maif.otoroshi.daikoku.domain.{ActualOtoroshiApiKey, ApiSubscription, OtoroshiSettings, Tenant, json} +import fr.maif.otoroshi.daikoku.domain.{ + ActualOtoroshiApiKey, + ApiSubscription, + OtoroshiSettings, + Tenant, + json +} import fr.maif.otoroshi.daikoku.env.Env import fr.maif.otoroshi.daikoku.logger.AppLogger import play.api.libs.json._ @@ -291,7 +300,9 @@ class OtoroshiClient(env: Env) { } } - def getSubscriptionLastUsage(subscriptions: Seq[ApiSubscription])(implicit otoroshiSettings: OtoroshiSettings, tenant: Tenant): EitherT[Future, JsArray, JsArray] = { + def getSubscriptionLastUsage(subscriptions: Seq[ApiSubscription])( + implicit otoroshiSettings: OtoroshiSettings, + tenant: Tenant): EitherT[Future, JsArray, JsArray] = { otoroshiSettings.elasticConfig match { case Some(config) => new ElasticReadsAnalytics(config, env) @@ -300,42 +311,48 @@ class OtoroshiClient(env: Env) { "bool" -> Json.obj( "filter" -> Json.arr( Json.obj("terms" -> Json.obj( - "identity.identity" -> JsArray(subscriptions.map(_.apiKey.clientId).map(JsString)) + "identity.identity" -> JsArray( + subscriptions.map(_.apiKey.clientId).map(JsString)) )) ) ) ), - "aggs" -> Json.obj( - "lastUsages" -> Json.obj( - "terms" -> Json.obj( - "field" -> "identity.identity" - ), - "aggs" -> Json.obj( - "latest" -> Json.obj( - "top_hits" -> Json.obj( - "size" -> 1, - "sort" -> Json.arr(Json.obj( - "@timestamp" -> Json.obj( - "order" -> "desc" - ) - )) - ) + "aggs" -> Json.obj("lastUsages" -> Json.obj( + "terms" -> Json.obj( + "field" -> "identity.identity" + ), + "aggs" -> Json.obj( + "latest" -> Json.obj( + "top_hits" -> Json.obj( + "size" -> 1, + "sort" -> Json.arr(Json.obj( + "@timestamp" -> Json.obj( + "order" -> "desc" + ) + )) ) ) - )), + ) + )), "size" -> 0 )) .map(resp => { - val buckets = (resp \ "aggregations" \ "lastUsages" \ "buckets").as[JsArray] + val buckets = + (resp \ "aggregations" \ "lastUsages" \ "buckets").as[JsArray] JsArray(buckets.value.map(agg => { val key = (agg \ "key").as[String] - val lastUsage = (agg \ "latest" \ "hits" \ "hits").as[JsArray].value.head + val lastUsage = + (agg \ "latest" \ "hits" \ "hits").as[JsArray].value.head val date = (lastUsage \ "_source" \ "@timestamp").as[JsValue] Json.obj( "clientName" -> key, "date" -> date, - "subscription" -> subscriptions.find(_.apiKey.clientId == key).map(_.id.asJson).getOrElse(JsNull).as[JsValue] + "subscription" -> subscriptions + .find(_.apiKey.clientId == key) + .map(_.id.asJson) + .getOrElse(JsNull) + .as[JsValue] ) })) }) @@ -343,25 +360,34 @@ class OtoroshiClient(env: Env) { AppLogger.error(e.getErrorMessage()) Json.arr() }) - case None => for { - elasticConfig <- EitherT.fromOptionF(getElasticConfig(), Json.arr()) - updatedSettings = otoroshiSettings.copy(elasticConfig = elasticConfig.some) - updatedTenant = tenant.copy(otoroshiSettings = tenant.otoroshiSettings.filter(_.id != otoroshiSettings.id) + updatedSettings) - _ <- EitherT.liftF(env.dataStore.tenantRepo.save(updatedTenant)) - r <- getSubscriptionLastUsage(subscriptions)(updatedSettings, updatedTenant) - } yield r + case None => + for { + elasticConfig <- EitherT.fromOptionF(getElasticConfig(), Json.arr()) + updatedSettings = otoroshiSettings.copy( + elasticConfig = elasticConfig.some) + updatedTenant = tenant.copy( + otoroshiSettings = tenant.otoroshiSettings.filter( + _.id != otoroshiSettings.id) + updatedSettings) + _ <- EitherT.liftF(env.dataStore.tenantRepo.save(updatedTenant)) + r <- getSubscriptionLastUsage(subscriptions)(updatedSettings, + updatedTenant) + } yield r } } - private def getElasticConfig()(implicit otoroshiSettings: OtoroshiSettings): Future[Option[ElasticAnalyticsConfig]] = { - client(s"/api/globalconfig").get().map(resp => { - if (resp.status == 200) { - val config = resp.json.as[JsObject] - val elasticReadConfig = (config \ "elasticReadsConfig").asOpt(ElasticAnalyticsConfig.format) - elasticReadConfig - } else { - None - } - }) + private def getElasticConfig()(implicit otoroshiSettings: OtoroshiSettings) + : Future[Option[ElasticAnalyticsConfig]] = { + client(s"/api/globalconfig") + .get() + .map(resp => { + if (resp.status == 200) { + val config = resp.json.as[JsObject] + val elasticReadConfig = + (config \ "elasticReadsConfig").asOpt(ElasticAnalyticsConfig.format) + elasticReadConfig + } else { + None + } + }) } } diff --git a/daikoku/javascript/src/services/index.ts b/daikoku/javascript/src/services/index.ts index d1b834f99..4b1e00643 100644 --- a/daikoku/javascript/src/services/index.ts +++ b/daikoku/javascript/src/services/index.ts @@ -1166,7 +1166,7 @@ export const importApiPages = ( method: 'PUT', body: JSON.stringify({ pages, - linked + linked, }), }); @@ -1182,7 +1182,7 @@ export const importPlanPages = ( method: 'PUT', body: JSON.stringify({ pages, - linked + linked, }), }); @@ -1948,12 +1948,15 @@ export const fetchInvoices = (teamId: string, apiId: string, planId: string, cal customFetch(`/api/teams/${teamId}/apis/${apiId}/plan/${planId}/invoices?callback=${callback}`); export type ILastUsage = { - clientName: string - date: number - subscription: string -} -export const getSubscriptionsLastUsages = (teamId: string, subscriptions: Array): PromiseWithError> => + clientName: string; + date: number; + subscription: string; +}; +export const getSubscriptionsLastUsages = ( + teamId: string, + subscriptions: Array +): PromiseWithError> => customFetch(`/api/teams/${teamId}/subscriptions/_lastUsage`, { method: 'POST', - body: JSON.stringify({subscriptions}) - }) + body: JSON.stringify({ subscriptions }), + }); diff --git a/daikoku/javascript/src/types/api.ts b/daikoku/javascript/src/types/api.ts index 0cb399ca5..42a3ff17c 100644 --- a/daikoku/javascript/src/types/api.ts +++ b/daikoku/javascript/src/types/api.ts @@ -332,7 +332,7 @@ export interface IDocPage { remoteContentEnabled: boolean; remoteContentUrl: string | null; remoteContentHeaders: object; - linked?: boolean + linked?: boolean; } export interface IOtoroshiApiKey { From e5f4da3bc32a111f9b45d7ce6174c020f57e45ed Mon Sep 17 00:00:00 2001 From: daikoku-github-actions Date: Tue, 7 Nov 2023 15:19:51 +0000 Subject: [PATCH 06/11] Update version number before release --- manual/src/main/paradox/getdaikoku/fromdocker.md | 2 +- manual/src/main/paradox/index.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manual/src/main/paradox/getdaikoku/fromdocker.md b/manual/src/main/paradox/getdaikoku/fromdocker.md index 8faf2e888..5df0fbdb7 100644 --- a/manual/src/main/paradox/getdaikoku/fromdocker.md +++ b/manual/src/main/paradox/getdaikoku/fromdocker.md @@ -5,7 +5,7 @@ If you're a Docker aficionado, Daikoku is provided as a Docker image that your c Fetch the last Docker image of Daikoku : ```sh -docker pull maif/daikoku:16.3.3 +docker pull maif/daikoku:16.3.4 # or docker pull maif/daikoku:latest ``` \ No newline at end of file diff --git a/manual/src/main/paradox/index.md b/manual/src/main/paradox/index.md index 04d6109b7..9c4050319 100644 --- a/manual/src/main/paradox/index.md +++ b/manual/src/main/paradox/index.md @@ -7,7 +7,7 @@ Since the v1.5, Daikoku & Otoroshi share the same Major & Minor version, so, ple > *In Japan, Daikokuten (大黒天), the god of great darkness or blackness, or the god of five cereals, is one of the Seven Lucky Gods (Fukujin). Daikokuten evolved from the Buddhist form of the Indian deity Shiva intertwined with the Shinto god Ōkuninushi. The name is the Japanese equivalent of Mahākāla, the Hindu name for Shiva.* @@@ div { .centered-img } -[![Build](https://github.com/MAIF/daikoku/workflows/Build/badge.svg)](https://travis-ci.org/MAIF/daikoku) [![Join the chat at https://gitter.im/MAIF/daikoku](https://badges.gitter.im/MAIF/daikoku.svg)](https://gitter.im/MAIF/daikoku?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [ ![Download](https://img.shields.io/github/release/MAIF/daikoku.svg) ](https://github.com/MAIF/daikoku/releases/download/v16.3.3/daikoku.jar) +[![Build](https://github.com/MAIF/daikoku/workflows/Build/badge.svg)](https://travis-ci.org/MAIF/daikoku) [![Join the chat at https://gitter.im/MAIF/daikoku](https://badges.gitter.im/MAIF/daikoku.svg)](https://gitter.im/MAIF/daikoku?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [ ![Download](https://img.shields.io/github/release/MAIF/daikoku.svg) ](https://github.com/MAIF/daikoku/releases/download/v16.3.4/daikoku.jar) @@@ @@@ div { .centered-img } @@ -20,12 +20,12 @@ You are not sure of what is **Otoroshi**, you should probably have a look at [th ## Installation -You can download the latest build of Daikoku as a [fat jar](https://github.com/MAIF/daikoku/releases/download/v16.3.3/daikoku.jar) or as a [zip package](https://github.com/MAIF/daikoku/releases/download/v16.3.3/daikoku-16.3.3.zip). +You can download the latest build of Daikoku as a [fat jar](https://github.com/MAIF/daikoku/releases/download/v16.3.4/daikoku.jar) or as a [zip package](https://github.com/MAIF/daikoku/releases/download/v16.3.4/daikoku-16.3.4.zip). You can install and run Otoroshi with this little bash snippet : ```sh -curl -L -o daikoku.jar 'https://github.com/MAIF/daikoku/releases/download/v16.3.3/daikoku.jar' +curl -L -o daikoku.jar 'https://github.com/MAIF/daikoku/releases/download/v16.3.4/daikoku.jar' java -jar daikoku.jar ``` From 19109604aa4fbc3d8d59d2d847228e4378851b8c Mon Sep 17 00:00:00 2001 From: daikoku-github-actions Date: Tue, 7 Nov 2023 15:20:10 +0000 Subject: [PATCH 07/11] Setting version to 16.3.4 --- manual/version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/version.sbt b/manual/version.sbt index ca3363b4f..c629f301e 100644 --- a/manual/version.sbt +++ b/manual/version.sbt @@ -1 +1 @@ -ThisBuild / version := "16.3.3" +ThisBuild / version := "16.3.4" From 929e9ce3b27d609b4d04ac772e739a88ec55032c Mon Sep 17 00:00:00 2001 From: daikoku-github-actions Date: Tue, 7 Nov 2023 15:20:26 +0000 Subject: [PATCH 08/11] Update documentation before release --- docs/manual/about.html | 6 +++--- docs/manual/adminusage/1-tenants.html | 6 +++--- docs/manual/adminusage/2-users.html | 6 +++--- docs/manual/adminusage/3-sessions.html | 6 +++--- docs/manual/adminusage/4-importexport.html | 6 +++--- docs/manual/adminusage/index.html | 6 +++--- docs/manual/apis.html | 6 +++--- docs/manual/archi.html | 6 +++--- docs/manual/consumerusage/1-subscribe.html | 6 +++--- docs/manual/consumerusage/2-apikeys.html | 6 +++--- .../consumerusage/3-aggregation-of-apikeys.html | 6 +++--- docs/manual/consumerusage/4-billing.html | 6 +++--- docs/manual/consumerusage/5-fastmode.html | 6 +++--- docs/manual/consumerusage/index.html | 6 +++--- docs/manual/content-pretty.json | 4 ++-- docs/manual/content.json | 2 +- docs/manual/deploy/index.html | 6 +++--- docs/manual/firstrun/configfile.html | 6 +++--- docs/manual/firstrun/datastore.html | 6 +++--- docs/manual/firstrun/env.html | 6 +++--- docs/manual/firstrun/index.html | 6 +++--- docs/manual/firstrun/initialstate.html | 6 +++--- docs/manual/firstrun/run.html | 6 +++--- docs/manual/getdaikoku/frombinaries.html | 6 +++--- docs/manual/getdaikoku/fromdocker.html | 8 ++++---- docs/manual/getdaikoku/fromsources.html | 6 +++--- docs/manual/getdaikoku/index.html | 6 +++--- docs/manual/index.html | 12 ++++++------ docs/manual/integrations.html | 6 +++--- docs/manual/paradox.json | 2 +- docs/manual/producerusage/1-apis.html | 6 +++--- docs/manual/producerusage/2-members.html | 6 +++--- docs/manual/producerusage/2-subscriptions.html | 6 +++--- docs/manual/producerusage/3-assets.html | 6 +++--- docs/manual/producerusage/4-income.html | 6 +++--- docs/manual/producerusage/index.html | 6 +++--- docs/manual/quickstart.html | 6 +++--- docs/manual/setup/admin.html | 6 +++--- docs/manual/setup/index.html | 6 +++--- docs/manual/tenantusage/1-otoroshi.html | 6 +++--- docs/manual/tenantusage/1.5-initialize.html | 6 +++--- docs/manual/tenantusage/2-teams.html | 6 +++--- docs/manual/tenantusage/3-assets.html | 6 +++--- docs/manual/tenantusage/4-messages.html | 6 +++--- docs/manual/tenantusage/5-translations.html | 6 +++--- docs/manual/tenantusage/5.5-display.html | 6 +++--- docs/manual/tenantusage/6-cms.html | 6 +++--- docs/manual/tenantusage/index.html | 6 +++--- manual/src/main/paradox/content-pretty.json | 4 ++-- manual/src/main/paradox/content.json | 2 +- 50 files changed, 146 insertions(+), 146 deletions(-) diff --git a/docs/manual/about.html b/docs/manual/about.html index ad24c3bee..45f08defe 100644 --- a/docs/manual/about.html +++ b/docs/manual/about.html @@ -30,7 +30,7 @@ Daikoku
-16.3.3 +16.3.4