diff --git a/Backend/package-lock.json b/Backend/package-lock.json index ff22d26..45df159 100644 --- a/Backend/package-lock.json +++ b/Backend/package-lock.json @@ -23,6 +23,7 @@ "csrf": "^3.1.0", "ethers": "^6.15.0", "ioredis": "^5.11.1", + "js-yaml": "^5.0.0", "pg": "^8.13.3", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", @@ -40,6 +41,7 @@ "@types/express": "^5.0.0", "@types/geojson": "^7946.0.16", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.10.7", "@types/sanitize-html": "^2.13.0", "@types/supertest": "^6.0.2", @@ -890,6 +892,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/js": { "version": "9.39.4", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", @@ -3535,6 +3560,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -5834,6 +5866,29 @@ } } }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -8771,10 +8826,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.0.0.tgz", + "integrity": "sha512-GSvaPUbk1U+FMZ7rJzF+F8e5YVtu7KnD40et/5rBXXRBv2jCO9L3qCewvIDDdudC0QycTFlf6EAA+h3kxBsuUw==", "funding": [ { "type": "github", @@ -8790,7 +8844,7 @@ "argparse": "^2.0.1" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "js-yaml": "bin/js-yaml.mjs" } }, "node_modules/jsesc": { diff --git a/Backend/package.json b/Backend/package.json index 581526b..19c7e6b 100644 --- a/Backend/package.json +++ b/Backend/package.json @@ -20,7 +20,8 @@ "test:e2e": "jest --config ./test/jest-e2e.json", "migration:run": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:run", "migration:revert": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:revert", - "migration:generate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:generate" + "migration:generate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:generate", + "export-spec": "ts-node -r tsconfig-paths/register src/export-spec.ts" }, "lint-staged": { "*.ts": "eslint --fix" @@ -40,6 +41,7 @@ "csrf": "^3.1.0", "ethers": "^6.15.0", "ioredis": "^5.11.1", + "js-yaml": "^5.0.0", "pg": "^8.13.3", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", @@ -57,6 +59,7 @@ "@types/express": "^5.0.0", "@types/geojson": "^7946.0.16", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.10.7", "@types/sanitize-html": "^2.13.0", "@types/supertest": "^6.0.2", diff --git a/Backend/src/export-spec.ts b/Backend/src/export-spec.ts new file mode 100644 index 0000000..93fccfb --- /dev/null +++ b/Backend/src/export-spec.ts @@ -0,0 +1,35 @@ +import { NestFactory } from '@nestjs/core'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { writeFileSync, mkdirSync } from 'fs'; +import { join } from 'path'; +import { dump } from 'js-yaml'; +import { AppModule } from './app.module'; + +const docsDir = join(__dirname, '..', '..', 'infrastructure', 'docs'); + +(async () => { + try { + const app = await NestFactory.create(AppModule, { logger: false }); + await app.init(); + + const swaggerConfig = new DocumentBuilder() + .setTitle('Gist API') + .setDescription('Anonymous hyperlocal messaging on Stellar') + .setVersion('0.1.0') + .build(); + + const document = SwaggerModule.createDocument(app, swaggerConfig); + + mkdirSync(docsDir, { recursive: true }); + writeFileSync( + join(docsDir, 'openapi.json'), + JSON.stringify(document, null, 2), + ); + writeFileSync(join(docsDir, 'openapi.yaml'), dump(document)); + + await app.close(); + } catch (error) { + console.error(error); + process.exit(1); + } +})(); diff --git a/infrastructure/ci/docs-generation.yml b/infrastructure/ci/docs-generation.yml index 7347e85..b44a40e 100644 --- a/infrastructure/ci/docs-generation.yml +++ b/infrastructure/ci/docs-generation.yml @@ -19,17 +19,56 @@ jobs: generate-api-docs: name: API Docs runs-on: ubuntu-latest + services: + postgres: + image: postgres:15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: vertexchain_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: Backend/package-lock.json + + - name: Install Backend dependencies + run: cd Backend && npm ci + - name: Generate OpenAPI and static docs + env: + DATABASE_HOST: localhost + DATABASE_PORT: 5432 + DATABASE_USER: postgres + DATABASE_PASSWORD: postgres + DATABASE_NAME: vertexchain_test run: | version="${{ github.event.release.tag_name }}" if [ -z "$version" ]; then version="$(date +%Y.%m.%d)"; fi bash infrastructure/scripts/generate-api-docs.sh "$version" + - name: Upload OpenAPI spec artifact + uses: actions/upload-artifact@v4 + with: + name: openapi-spec + path: | + infrastructure/docs/openapi.json + infrastructure/docs/openapi.yaml + if-no-files-found: error + - name: Commit API docs run: | git config user.name "github-actions[bot]" diff --git a/infrastructure/scripts/generate-api-docs.sh b/infrastructure/scripts/generate-api-docs.sh index cbf1288..21b3272 100755 --- a/infrastructure/scripts/generate-api-docs.sh +++ b/infrastructure/scripts/generate-api-docs.sh @@ -17,18 +17,16 @@ mkdir -p "$(dirname "${spec_output}")" "${site_output}" "$(dirname "${version_fi version="${1:-$(date +%Y.%m.%d)}" echo "${version}" > "${version_file}" -cat > "${spec_output}" < "${site_output}/index.html" < "${site_output}/index.html" < VertexChain API Docs @@ -43,6 +41,6 @@ cat > "${site_output}/index.html" < -EOF +HTMLEOF echo "Generated API docs for version ${version}"