Markdown docker compose (#192)
* Add RetrieveDockerCompose API endpoint * updated license details component added docker compose config * precommit fixes --------- Co-authored-by: Davide Di Modica <webmaster.ddm@gmail.com>
taylor_socfortress committed
Apr 17, 2024 at 13:54 UTC
b35c81d14144c6ec2be16a2748b1efae7a00ccd7
5 files changed
+102
-3
backend/app/middleware/license.py
+42
@@ -314,6 +314,12 @@ class CancelSubscriptionResponse(BaseModel):
314
message: str
315
316
317
+class RetrieveDockerCompose(BaseModel):
318
+ docker_compose: str
319
+ success: bool
320
+ message: str
321
+
322
+
323
license_router = APIRouter()
324
325
@@ -824,6 +830,42 @@ async def replace_license_in_db(request: ReplaceLicenseRequest, session: AsyncSe
830
raise HTTPException(status_code=400, detail="License replacement failed")
831
832
833
+@license_router.post(
834
+ "/retrieve-docker-compose",
835
+ response_model=RetrieveDockerCompose,
836
+ description="Retrieve Docker Compose for features enabled",
837
+)
838
+async def retrieve_docker_compose(session: AsyncSession = Depends(get_db)) -> RetrieveDockerCompose:
839
+ """
840
+ Retrieve the Docker Compose for features enabled in the license.
841
+
842
+ Args:
843
+ session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
844
+
845
+ Returns:
846
+ RetrieveDockerCompose: A Pydantic model containing the Docker Compose for features enabled.
847
+ """
848
+ try:
849
+ license = await get_license(session)
850
+ if license.license_key:
851
+ results = await send_post_request("retrieve-docker-compose", data={"license_key": license.license_key})
852
+ logger.info(f"Results: {results}")
853
+ return RetrieveDockerCompose(
854
+ docker_compose=results["data"]["docker_compose"],
855
+ success=results["success"],
856
+ message=results["message"],
857
+ )
858
+ else:
859
+ return RetrieveDockerCompose(
860
+ docker_compose="",
861
+ success=False,
862
+ message="License key not found",
863
+ )
864
+ except Exception as e:
865
+ logger.error(e)
866
+ raise HTTPException(status_code=400, detail="Failed to retrieve Docker Compose")
867
+
868
+
869
def create_headers(request: ThreatIntelRegisterRequest) -> Dict[str, str]:
870
return {
871
"x-api-key": request.requesting_api_key,
frontend/src/api/license.ts
+3
@@ -56,6 +56,9 @@ export default {
56
cancelSubscription(payload: CancelSubscriptionPayload) {
57
return HttpClient.post<FlaskBaseResponse>(`/license/cancel_subscription`, payload)
58
},
59
+ retrieveDockerCompose() {
60
+ return HttpClient.post<FlaskBaseResponse & { docker_compose: string }>(`/license/retrieve-docker-compose`)
61
+ },
62
// TODO: remove, deprecated
63
extendLicense(period: number) {
64
return HttpClient.post<FlaskBaseResponse>(
frontend/src/components/common/Markdown.vue
+15
-1
@@ -4,6 +4,7 @@
4
preset="commonmark"
5
:plugins="[[markdownItHighlightjs, { hljs, auto: true, code: true, inline: false, ignoreIllegals: true }]]"
6
class="markdown-style scrollbar-styled"
7
+ :class="{ transparent }"
8
/>
9
</template>
10
@@ -18,16 +19,19 @@ import powershell from "highlight.js/lib/languages/powershell"
19
import bash from "highlight.js/lib/languages/bash"
20
import json from "highlight.js/lib/languages/json"
21
import xml from "highlight.js/lib/languages/xml"
22
+import yaml from "highlight.js/lib/languages/yaml"
23
24
hljs.registerLanguage("powershell", powershell)
25
hljs.registerLanguage("bash", bash)
26
hljs.registerLanguage("json", json)
27
hljs.registerLanguage("xml", xml)
28
+hljs.registerLanguage("yaml", yaml)
29
30
const props = defineProps<{
31
source: string
32
+ transparent?: boolean
33
}>()
30
-const { source } = toRefs(props)
34
+const { source, transparent } = toRefs(props)
35
</script>
36
37
<style lang="scss" scoped>
@@ -37,5 +41,15 @@ const { source } = toRefs(props)
41
margin-bottom: 15px;
42
}
43
}
44
+
45
+ &.transparent {
46
+ :deep() {
47
+ & > pre {
48
+ & > code {
49
+ background-color: transparent;
50
+ }
51
+ }
52
+ }
53
+ }
54
}
55
</style>
frontend/src/components/license/LicenseDetails.vue
+41
-2
@@ -64,6 +64,17 @@
64
</div>
65
</template>
66
</KVCard>
67
+ <KVCard class="!basis-auto" v-if="dockerCompose">
68
+ <template #key>
69
+ <span class="flex gap-3 items-center">
70
+ <Icon :name="ConfigIcon" :size="14"></Icon>
71
+ <span>Docker Configuration</span>
72
+ </span>
73
+ </template>
74
+ <template #value>
75
+ <Markdown :source="dockerCompose" transparent />
76
+ </template>
77
+ </KVCard>
78
</div>
79
</n-spin>
80
</template>
@@ -72,13 +83,14 @@
83
import { NSpin, useMessage } from "naive-ui"
84
import Icon from "@/components/common/Icon.vue"
85
import Api from "@/api"
75
-import { onBeforeMount, onMounted, ref, toRefs, computed } from "vue"
86
+import { onBeforeMount, onMounted, ref, toRefs, computed, defineAsyncComponent } from "vue"
87
import { type LicenseFeatures, type License } from "@/types/license.d"
88
import { formatDate } from "@/utils"
89
import { useSettingsStore } from "@/stores/settings"
90
import _startCase from "lodash/startCase"
91
import Badge from "@/components/common/Badge.vue"
92
import KVCard from "@/components/common/KVCard.vue"
93
+const Markdown = defineAsyncComponent(() => import("@/components/common/Markdown.vue"))
94
95
const emit = defineEmits<{
96
(e: "licenseLoaded", value: License): void
@@ -103,14 +115,17 @@ const ExpiresIcon = "ph:calendar-blank"
115
const CustomerIcon = "carbon:user"
116
const CheckIcon = "carbon:checkmark-outline"
117
const FeaturesIcon = "material-symbols:checklist"
118
+const ConfigIcon = "carbon:settings"
119
120
const message = useMessage()
121
const loadingLicense = ref(false)
122
const loadingFeatures = ref(false)
123
+const loadingDockerCompose = ref(false)
124
const dFormats = useSettingsStore().dateFormat
125
126
const licenseLoaded = ref<License | null>(null)
127
const featuresLoaded = ref<LicenseFeatures[]>([])
128
+const dockerCompose = ref<string | null>(null)
129
const license = computed(() => licenseLoaded.value || licenseData?.value || null)
130
const features = computed(() => featuresLoaded.value || featuresData?.value || [])
131
const expiresText = computed(() => (license.value ? formatDate(license.value.expires, dFormats.datetime) : ""))
@@ -118,7 +133,7 @@ const periodText = computed(() =>
133
license.value ? `${license.value.period} Day${license.value.period === 1 ? "" : "s"}` : ""
134
)
135
121
-const loading = computed(() => loadingLicense.value || loadingFeatures.value)
136
+const loading = computed(() => loadingLicense.value || loadingFeatures.value || loadingDockerCompose.value)
137
138
function getLicense() {
139
loadingLicense.value = true
@@ -165,6 +180,28 @@ function getLicenseFeatures() {
180
})
181
}
182
183
+function retrieveDockerCompose() {
184
+ loadingDockerCompose.value = true
185
+
186
+ Api.license
187
+ .retrieveDockerCompose()
188
+ .then(res => {
189
+ if (res.data.success) {
190
+ dockerCompose.value = res.data?.docker_compose
191
+ } else {
192
+ message.warning(res.data?.message || "An error occurred. Please try again later.")
193
+ }
194
+ })
195
+ .catch(err => {
196
+ if (err.response.status !== 404) {
197
+ message.error(err.response?.data?.message || "An error occurred. Please try again later.")
198
+ }
199
+ })
200
+ .finally(() => {
201
+ loadingDockerCompose.value = false
202
+ })
203
+}
204
+
205
function load() {
206
if (!license.value) {
207
getLicense()
@@ -172,6 +209,8 @@ function load() {
209
if (!hideFeatures.value && !features.value.length) {
210
getLicenseFeatures()
211
}
212
+
213
+ retrieveDockerCompose()
214
}
215
216
function sanitizeKey(text: string) {
frontend/src/components/license/LicenseViewer.vue
+1
@@ -52,6 +52,7 @@ function licenseLoaded(license: License) {
52
.side-box {
53
overflow: hidden;
54
transition: all 0.3s var(--bezier-ease);
55
+ border-radius: var(--border-radius);
56
}
57
&.has-side {
58
align-items: stretch;