mirror of
https://github.com/coollabsio/coolify-docs.git
synced 2026-06-19 07:35:55 +00:00
feat: changes of vitepress folder structure and openapi sidebar, including openapi dynamic pages
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
import yaml from 'vite-plugin-yaml'
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { useSidebar, useOpenapi } from 'vitepress-openapi'
|
||||
import spec from '../public/openapi.json' assert { type: 'json' }
|
||||
|
||||
const sidebar = useSidebar({ spec, collapsible: true })
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
lang: 'en-US',
|
||||
@@ -311,10 +316,34 @@ export default defineConfig({
|
||||
collapsed: true,
|
||||
link: '/api-reference/api/overview',
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/api-reference/api/overview'
|
||||
}
|
||||
...sidebar.generateSidebarGroups({
|
||||
/**
|
||||
* Optionally, you can filter paths by a prefix. Default is an empty string.
|
||||
*/
|
||||
startsWith: 'operations',
|
||||
|
||||
/**
|
||||
* Optionally, you can specify if the sidebar items are collapsible. Default is true.
|
||||
*/
|
||||
collapsible: true,
|
||||
|
||||
/**
|
||||
* Optionally, you can specify a depth for the sidebar items. Default is 6, which is the maximum VitePress sidebar depth.
|
||||
*/
|
||||
depth: 6,
|
||||
|
||||
/**
|
||||
* Optionally, you can specify a link prefix for all generated sidebar items. Default is `/operations/`.
|
||||
*/
|
||||
linkPrefix: '/api-reference/api/operations/',
|
||||
|
||||
/**
|
||||
* Optionally, you can specify a template for the sidebar items. You can see the default value
|
||||
* in `sidebarItemTemplate` function in the `useSidebar` composable.
|
||||
*/
|
||||
//sidebarItemTemplate: (method: string, path: string): string => `[${method}] ${path}`,
|
||||
}),
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -13,9 +13,8 @@ import "./style.css";
|
||||
import "./tailwind.postcss";
|
||||
import "vitepress-openapi/dist/style.css";
|
||||
|
||||
// Load the OpenAPI spec
|
||||
import { load } from 'js-yaml'
|
||||
import rawSpec from './openapi.yml?raw'
|
||||
// @ts-ignore
|
||||
import spec from '../../public/openapi.json' assert { type: 'json' }
|
||||
|
||||
// Import components
|
||||
import Card from "./components/Card.vue";
|
||||
@@ -35,13 +34,12 @@ export default {
|
||||
extends: DefaultTheme,
|
||||
Layout: Landing,
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
const spec = load(rawSpec)
|
||||
const openapi = useOpenapi({
|
||||
spec,
|
||||
base: "/docs/api-reference/api/overview/",
|
||||
label: "API",
|
||||
base: "/docs/api-reference/api/operations/",
|
||||
label: "API"
|
||||
});
|
||||
// Use theme.enhanceApp with both app and openapi
|
||||
|
||||
theme.enhanceApp({ app, openapi });
|
||||
app.component("Card", Card);
|
||||
app.component("CardGroup", CardGroup);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
---
|
||||
aside: false
|
||||
outline: false
|
||||
title: API Reference
|
||||
toc: false
|
||||
---
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
import { useTheme, generateCodeSample } from 'vitepress-openapi'
|
||||
|
||||
const { isDark } = useData()
|
||||
|
||||
useTheme({
|
||||
codeSamples: {
|
||||
langs: [
|
||||
'bruno',
|
||||
...useTheme().getCodeSamplesLangs(),
|
||||
],
|
||||
availableLanguages: [
|
||||
{
|
||||
lang: 'bruno',
|
||||
label: 'Bruno',
|
||||
highlighter: 'plaintext',
|
||||
},
|
||||
...useTheme().getCodeSamplesAvailableLanguages(),
|
||||
],
|
||||
defaultLang: 'bruno',
|
||||
generator: (lang, request) => {
|
||||
if (lang === 'bruno') {
|
||||
return generateBruRequest(request)
|
||||
}
|
||||
|
||||
return generateCodeSample(lang, request)
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
function generateBruRequest(request) {
|
||||
// ... existing generateBruRequest code ...
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ... existing styles ... */
|
||||
</style>
|
||||
|
||||
<OASpec :isDark="isDark" />
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
aside: false
|
||||
outline: false
|
||||
title: API Operation - {{ $params.operation }}
|
||||
toc: false
|
||||
---
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
import { useTheme, generateCodeSample } from 'vitepress-openapi'
|
||||
|
||||
const { params, isDark } = useData()
|
||||
const operation = params.value.operation
|
||||
|
||||
const themeConfig = {
|
||||
codeSamples: {
|
||||
langs: [
|
||||
'bruno',
|
||||
...useTheme().getCodeSamplesLangs(),
|
||||
],
|
||||
availableLanguages: [
|
||||
{
|
||||
lang: 'bruno',
|
||||
label: 'Bruno',
|
||||
highlighter: 'plaintext',
|
||||
},
|
||||
...useTheme().getCodeSamplesAvailableLanguages(),
|
||||
],
|
||||
defaultLang: 'bruno',
|
||||
generator: (lang, request) => {
|
||||
if (lang === 'bruno') {
|
||||
return generateBrunoRequest(request) || ''
|
||||
}
|
||||
return generateCodeSample(lang, request) || ''
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function generateBrunoRequest(request) {
|
||||
if (!request) return ''
|
||||
const { method, url, headers, body } = request
|
||||
let brunoScript = `${method} ${url}\n`
|
||||
|
||||
if (headers && Object.keys(headers).length) {
|
||||
brunoScript += '\nHeaders\n'
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
brunoScript += `${key}: ${value}\n`
|
||||
}
|
||||
}
|
||||
|
||||
if (body) {
|
||||
brunoScript += '\nBody\n'
|
||||
brunoScript += typeof body === 'string' ? body : JSON.stringify(body, null, 2)
|
||||
}
|
||||
|
||||
return brunoScript
|
||||
}
|
||||
|
||||
useTheme(themeConfig)
|
||||
</script>
|
||||
|
||||
<OAOperation :operationId="operation" :isDark="isDark" />
|
||||
@@ -0,0 +1,21 @@
|
||||
import fs from 'fs'
|
||||
import spec from '../../../public/openapi.json' assert { type: 'json' }
|
||||
|
||||
export default {
|
||||
async paths() {
|
||||
// Extract all operationIds
|
||||
const operations: string[] = []
|
||||
Object.entries(spec.paths).forEach(([path, pathItem]: [string, Record<string, any>]) => {
|
||||
Object.entries(pathItem).forEach(([method, operation]: [string, { operationId?: string }]) => {
|
||||
if (operation?.operationId) {
|
||||
operations.push(operation.operationId)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Return the params array
|
||||
return operations.map(op => ({
|
||||
params: { operation: op }
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
---
|
||||
aside: false
|
||||
outline: false
|
||||
title: API
|
||||
toc: false
|
||||
---
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
import { useTheme, generateCodeSample } from 'vitepress-openapi'
|
||||
|
||||
const { isDark } = useData()
|
||||
|
||||
useTheme({
|
||||
codeSamples: {
|
||||
// List of languages to show in Code Samples section.
|
||||
langs: [
|
||||
'bruno',
|
||||
...useTheme().getCodeSamplesLangs(),
|
||||
],
|
||||
// List of available languages to select from.
|
||||
availableLanguages: [
|
||||
{
|
||||
lang: 'bruno',
|
||||
label: 'Bruno',
|
||||
highlighter: 'plaintext',
|
||||
},
|
||||
...useTheme().getCodeSamplesAvailableLanguages(),
|
||||
],
|
||||
defaultLang: 'bruno',
|
||||
generator: (lang, request) => {
|
||||
if (lang === 'bruno') {
|
||||
return generateBruRequest(request)
|
||||
}
|
||||
|
||||
return generateCodeSample(lang, request)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
function generateBruRequest(request) {
|
||||
const { url, method, headers, body, query } = request;
|
||||
|
||||
const methodLower = method.toLowerCase();
|
||||
|
||||
const queryString = query && Object.keys(query).length
|
||||
? `${url}?${new URLSearchParams(query).toString()}`
|
||||
: url;
|
||||
|
||||
const headersSection = headers && Object.keys(headers).length
|
||||
? `headers {\n${Object.entries(headers)
|
||||
.map(([key, value]) => ` ${key}: ${value}`)
|
||||
.join('\n')}\n}`
|
||||
: '';
|
||||
|
||||
const bodySection = body
|
||||
? `body {\n ${JSON.stringify(body, null, 2).replace(/\n/g, '\n ')}\n}`
|
||||
: '';
|
||||
|
||||
const bruRequest = `${methodLower} {
|
||||
url: ${queryString}
|
||||
}
|
||||
|
||||
${headersSection}
|
||||
|
||||
${bodySection}
|
||||
`;
|
||||
|
||||
return bruRequest
|
||||
.trim()
|
||||
.replace(/\n{2,}/g, '\n\n') // Remove extra newlines
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:deep(.vp-doc a),
|
||||
:deep([openapi] a),
|
||||
:deep(a.grid) {
|
||||
border-bottom: none !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
:deep(.vp-doc a:hover),
|
||||
:deep([openapi] a:hover),
|
||||
:deep(a.grid:hover) {
|
||||
color: var(--vp-c-brand-1);
|
||||
border-bottom: none !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
/* Additional specific override for grid links */
|
||||
:deep(a.grid.items-center) {
|
||||
border-bottom: none !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<OASpec :isDark="isDark" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -25,7 +25,7 @@
|
||||
"dev": "vitepress dev docs",
|
||||
"build": "vitepress build docs",
|
||||
"preview": "vitepress preview docs",
|
||||
"transform-compose": "tsx docs/.vitepress/data/transform-compose.ts"
|
||||
"transform-openapi": "tsx scripts/convert-openapi.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^12.2.0",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
const yaml = require('js-yaml')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const convertYamlToJson = () => {
|
||||
try {
|
||||
// Read the YAML file from the public folder
|
||||
const yamlPath = path.resolve(__dirname, '../docs/public/openapi.yml')
|
||||
const yamlContent = fs.readFileSync(yamlPath, 'utf8')
|
||||
|
||||
// Parse YAML to JSON
|
||||
const jsonContent = yaml.load(yamlContent)
|
||||
|
||||
// Write JSON to public folder next to the YAML file
|
||||
const jsonPath = path.resolve(__dirname, '../docs/public/openapi.json')
|
||||
fs.writeFileSync(jsonPath, JSON.stringify(jsonContent, null, 2))
|
||||
|
||||
console.log('Successfully converted YAML to JSON')
|
||||
console.log(`Output file: ${jsonPath}`)
|
||||
} catch (error) {
|
||||
console.error('Error converting YAML to JSON:', error)
|
||||
}
|
||||
}
|
||||
|
||||
convertYamlToJson()
|
||||
Reference in New Issue
Block a user