diff --git a/.env.development b/.env.development index 3394e78..c606e46 100644 --- a/.env.development +++ b/.env.development @@ -2,3 +2,8 @@ PORT=3000 NODE_ENV=development LOG_LEVEL=debug + +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_DB= +DATABASE_URL= \ No newline at end of file diff --git a/.env.example b/.env.example index 364d705..7932fb5 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,7 @@ PORT=3000 NODE_ENV=development LOG_LEVEL=info +POSTGRES_USER=your_username +POSTGRES_PASSWORD=your_password +POSTGRES_DB=your_database +DATABASE_URL=postgresql://your_username:your_password@localhost:5432/your_database \ No newline at end of file diff --git a/.env.production b/.env.production index 77b21b2..96464e3 100644 --- a/.env.production +++ b/.env.production @@ -2,3 +2,6 @@ PORT=80 NODE_ENV=production LOG_LEVEL=info +POSTGRES_USER=prod_username +POSTGRES_PASSWORD=prod_password +POSTGRES_DB=prod_database \ No newline at end of file diff --git a/.env.staging b/.env.staging index 40f6db1..e6b8508 100644 --- a/.env.staging +++ b/.env.staging @@ -2,3 +2,7 @@ PORT=3002 NODE_ENV=staging LOG_LEVEL=info +POSTGRES_USER=staging_username +POSTGRES_PASSWORD=staging_password +POSTGRES_DB=staging_database +DATABASE_URL=postgresql://staging_username:staging_password@localhost:5432/staging_database \ No newline at end of file diff --git a/.env.test b/.env.test index d5a39bb..b6b98bb 100644 --- a/.env.test +++ b/.env.test @@ -2,3 +2,6 @@ PORT=3001 NODE_ENV=test LOG_LEVEL=warn +POSTGRES_USER=test_user +POSTGRES_PASSWORD=test_password +POSTGRES_DB=test_database \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index c12ac8d..e37c998 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,7 +10,7 @@ module.exports = { jest: true, es2020: true, }, - plugins: ['@typescript-eslint'], + plugins: ['@typescript-eslint', 'prettier'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67f1cb3..9dfcd44 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,99 +1,160 @@ -name: chainremit +name: CI/CD Pipeline on: push: - branches: [main, develop] + branches: [ main, develop ] pull_request: - branches: [main, develop] + branches: [ main, develop ] + workflow_dispatch: + +env: + NODE_VERSION: '18' + # Database configuration - commented out as not currently used + # POSTGRES_USER: postgres + # POSTGRES_PASSWORD: password + # POSTGRES_DB: chainremit_db + # DATABASE_URL: postgresql://postgres:password@localhost:5432/chainremit_db jobs: - build: + quality: + name: Code Quality runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x, 20.x] steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run build - - name: Upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: build - path: dist - - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run lint - - run: npm run format + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - name: Install dependencies + run: npm ci + - name: Lint + run: npm run lint + - name: Check formatting + run: npm run format -- --check + - name: Security audit + run: npm audit fix test: + name: Tests + needs: quality runs-on: ubuntu-latest + # Database service - commented out as not currently used + # services: + # postgres: + # image: postgres:14 + # env: + # POSTGRES_USER: ${{ env.POSTGRES_USER }} + # POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }} + # POSTGRES_DB: ${{ env.POSTGRES_DB }} + # ports: + # - 5432:5432 + # options: >- + # --health-cmd pg_isready + # --health-interval 10s + # --health-timeout 5s + # --health-retries 5 steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run test:coverage - - name: Upload coverage report - uses: actions/upload-artifact@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - name: coverage - path: coverage - - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: npm ci - - run: npm audit --audit-level=high + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm run test + # Database-dependent tests - commented out + # - name: Run e2e tests + # run: npm run test:e2e + # env: + # DATABASE_URL: ${{ env.DATABASE_URL }} + # - name: Upload coverage reports + # uses: codecov/codecov-action@v3 + # with: + # token: ${{ secrets.CODECOV_TOKEN }} - deploy-staging: - if: github.ref == 'refs/heads/main' - needs: [build, lint, test, security] + build: + name: Build + needs: test runs-on: ubuntu-latest - environment: - name: staging steps: - - name: Deploy to Staging - run: echo "Deploying to Staging..." - # Add deployment script/command - - name: Notify Slack - if: always() - uses: 8398a7/action-slack@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - status: ${{ job.status }} - fields: repo,message,commit,author,action,eventName,ref,workflow,job,took - # env: - # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - - deploy-production: - if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' - needs: [deploy-staging] - runs-on: ubuntu-latest - environment: - name: production - steps: - - name: Deploy to Production - run: echo "Deploying to Production..." - # Add deployment script/command - - name: Notify Slack - if: always() - uses: 8398a7/action-slack@v3 + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - name: Install dependencies + run: npm ci + - name: Build + run: npm run build + - name: Upload build artifacts + uses: actions/upload-artifact@v4 with: - status: ${{ job.status }} - fields: repo,message,commit,author,action,eventName,ref,workflow,job,took - # env: - # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + name: dist + path: dist - dependabot: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Enable Dependabot - run: echo "Dependabot enabled for automated dependency updates." + # Deployment jobs remain commented out as they were + # deploy-staging: + # name: Deploy to Staging + # needs: build + # if: github.ref == 'refs/heads/develop' + # runs-on: ubuntu-latest + # environment: + # name: staging + # url: https://staging-api.chainremit.com + # steps: + # - uses: actions/checkout@v4 + # - name: Download build artifacts + # uses: actions/download-artifact@v3 + # with: + # name: dist + # path: dist + # - name: Configure AWS credentials + # uses: aws-actions/configure-aws-credentials@v4 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: ${{ secrets.AWS_REGION }} + # - name: Deploy to AWS Elastic Beanstalk + # run: | + # aws elasticbeanstalk create-application-version \ + # --application-name chainremit-backend \ + # --version-label "staging-${{ github.sha }}" \ + # --source-bundle S3Bucket="${{ secrets.EB_BUCKET }}",S3Key="staging-${{ github.sha }}.zip" + # aws elasticbeanstalk update-environment \ + # --environment-name chainremit-staging \ + # --version-label "staging-${{ github.sha }}" + + # deploy-production: + # name: Deploy to Production + # needs: build + # if: github.ref == 'refs/heads/main' + # runs-on: ubuntu-latest + # environment: + # name: production + # url: https://api.chainremit.com + # steps: + # - uses: actions/checkout@v4 + # - name: Download build artifacts + # uses: actions/download-artifact@v3 + # with: + # name: dist + # path: dist + # - name: Configure AWS credentials + # uses: aws-actions/configure-aws-credentials@v4 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: ${{ secrets.AWS_REGION }} + # - name: Deploy to AWS Elastic Beanstalk + # run: | + # aws elasticbeanstalk create-application-version \ + # --application-name chainremit-backend \ + # --version-label "prod-${{ github.sha }}" \ + # --source-bundle S3Bucket="${{ secrets.EB_BUCKET }}",S3Key="prod-${{ github.sha }}.zip" + # aws elasticbeanstalk update-environment \ + # --environment-name chainremit-production \ + # --version-label "prod-${{ github.sha }}" \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index faa4bf2..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: CodeQL - -on: - push: - branches: [main] - pull_request: - branches: [main] - schedule: - - cron: '0 0 * * 0' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript', 'typescript' ] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/coverage/lcov-report/base.css b/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/lcov-report/favicon.png b/coverage/lcov-report/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/lcov-report/favicon.png differ diff --git a/coverage/lcov-report/index.html b/coverage/lcov-report/index.html new file mode 100644 index 0000000..a2b7c50 --- /dev/null +++ b/coverage/lcov-report/index.html @@ -0,0 +1,101 @@ + + + + +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|