├── .dockerignore ├── .eslintrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── codeql.yml │ ├── docker-image.yml │ ├── docker-publish.yml │ ├── greetings.yml │ ├── label.yml │ ├── manual.yml │ ├── node.js.yml │ ├── npm-publish-github-packages.yml │ ├── npm-publish.yml │ ├── stale.yml │ ├── super-linter.yml │ ├── tencent.yml │ ├── test.yml │ └── webpack.yml ├── .gitignore ├── .nvmrc ├── .whitesource ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── SpaceXSpecs ├── App.js ├── RocketList.js └── RocketSpecs.js ├── app.js ├── docs ├── apps.md ├── clients.md └── v4 │ ├── README.md │ ├── capsules │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── company │ ├── all.md │ ├── schema.md │ └── update.md │ ├── cores │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── crew │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── dragons │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── landpads │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── launches │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── latest.md │ ├── next.md │ ├── one.md │ ├── past.md │ ├── query.md │ ├── schema.md │ ├── upcoming.md │ └── update.md │ ├── launchpads │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── payloads │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── queries.md │ ├── roadster │ ├── get.md │ ├── schema.md │ └── update.md │ ├── rockets │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ ├── ships │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md │ └── starlink │ ├── all.md │ ├── create.md │ ├── delete.md │ ├── one.md │ ├── query.md │ ├── schema.md │ └── update.md ├── jobs ├── capsules.js ├── cores.js ├── landpads.js ├── launches.js ├── launchpads.js ├── payloads.js ├── roadster.js ├── starlink.js ├── upcoming.js ├── webcast.js └── worker.js ├── middleware ├── auth.js ├── authz │ ├── index.js │ ├── model.conf │ └── policy.csv ├── cache.js ├── errors.js ├── index.js ├── logger.js └── response-time.js ├── package-lock.json ├── package.json ├── scripts └── healthcheck.js ├── server.js ├── services ├── index.js └── v4 │ ├── admin │ └── routes.js │ ├── capsules │ ├── model.js │ └── routes.js │ ├── company │ ├── model.js │ └── routes.js │ ├── cores │ ├── model.js │ └── routes.js │ ├── crew │ ├── model.js │ └── routes.js │ ├── dragons │ ├── model.js │ └── routes.js │ ├── index.js │ ├── landpads │ ├── model.js │ └── routes.js │ ├── launches │ ├── model.js │ └── routes.js │ ├── launchpads │ ├── model.js │ └── routes.js │ ├── payloads │ ├── model.js │ └── routes.js │ ├── roadster │ ├── model.js │ └── routes.js │ ├── rockets │ ├── model.js │ └── routes.js │ ├── ships │ ├── model.js │ └── routes.js │ ├── starlink │ ├── model.js │ └── routes.js │ └── users │ ├── model.js │ └── routes.js ├── start.sh └── tests └── index.test.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | npm-debug.log 4 | *.md 5 | test 6 | docs 7 | Dockerfile 8 | coverage 9 | LICENSE 10 | .eslintrc 11 | .gitignore 12 | .nvmrc -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["security", "mongodb", "jest", "no-secrets"], 3 | "env": { 4 | "node": true, 5 | "jest": true, 6 | "mongo": true 7 | }, 8 | "extends": ["plugin:security/recommended", "airbnb-base"], 9 | "parserOptions": { 10 | "sourceType": "module", 11 | "ecmaVersion": 2020, 12 | "impliedStrict": true 13 | }, 14 | "rules": { 15 | "no-console": 0, 16 | "no-secrets/no-secrets": "error", 17 | "import/no-extraneous-dependencies": [ 18 | "error", 19 | { 20 | "devDependencies": ["test/*.test.js", "scripts/*.js"] 21 | } 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | assignees: 8 | - "jakewmeyer" -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master, ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 4 * * 2' 11 | 12 | jobs: 13 | analyse: 14 | name: Analyse 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v1 33 | # Override language selection by uncommenting this and choosing your languages 34 | # with: 35 | # languages: go, javascript, csharp, python, cpp, java 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v1 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v1 55 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '40 22 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | # Runner size impacts CodeQL analysis time. To learn more, please see: 27 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 28 | # - https://gh.io/supported-runners-and-hardware-resources 29 | # - https://gh.io/using-larger-runners 30 | # Consider using larger runners for possible analysis time improvements. 31 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 32 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 33 | permissions: 34 | actions: read 35 | contents: read 36 | security-events: write 37 | 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | language: [ 'javascript-typescript' ] 42 | # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] 43 | # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both 44 | # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 45 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 46 | 47 | steps: 48 | - name: Checkout repository 49 | uses: actions/checkout@v3 50 | 51 | # Initializes the CodeQL tools for scanning. 52 | - name: Initialize CodeQL 53 | uses: github/codeql-action/init@v2 54 | with: 55 | languages: ${{ matrix.language }} 56 | # If you wish to specify custom queries, you can do so here or in a config file. 57 | # By default, queries listed here will override any specified in a config file. 58 | # Prefix the list here with "+" to use these queries and those in the config file. 59 | 60 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 61 | # queries: security-extended,security-and-quality 62 | 63 | 64 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 65 | # If this step fails, then you should remove it and run the build manually (see below) 66 | - name: Autobuild 67 | uses: github/codeql-action/autobuild@v2 68 | 69 | # ℹ️ Command-line programs to run using the OS shell. 70 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 71 | 72 | # If the Autobuild fails above, remove it and uncomment the following three lines. 73 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 74 | 75 | # - run: | 76 | # echo "Run, Build Application using script" 77 | # ./location_of_script_within_repo/buildscript.sh 78 | 79 | - name: Perform CodeQL Analysis 80 | uses: github/codeql-action/analyze@v2 81 | with: 82 | category: "/language:${{matrix.language}}" 83 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Build the Docker image 18 | run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) 19 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/first-interaction@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-message: "Message that will be displayed on users' first issue" 16 | pr-message: "Message that will be displayed on users' first pull request" 17 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request_target] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/labeler@v4 21 | with: 22 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 23 | -------------------------------------------------------------------------------- /.github/workflows/manual.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: Manual workflow 4 | 5 | # Controls when the action will run. Workflow runs when manually triggered using the UI 6 | # or API. 7 | on: 8 | workflow_dispatch: 9 | # Inputs the workflow accepts. 10 | inputs: 11 | name: 12 | # Friendly description to be shown in the UI instead of 'name' 13 | description: 'Person to greet' 14 | # Default value if no value is explicitly provided 15 | default: 'World' 16 | # Input has to be provided for the workflow to run 17 | required: true 18 | # The data type of the input 19 | type: string 20 | 21 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 22 | jobs: 23 | # This workflow contains a single job called "greet" 24 | greet: 25 | # The type of runner that the job will run on 26 | runs-on: ubuntu-latest 27 | 28 | # Steps represent a sequence of tasks that will be executed as part of the job 29 | steps: 30 | # Runs a single command using the runners shell 31 | - name: Send greeting 32 | run: echo "Hello ${{ inputs.name }}" 33 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x, 16.x, 18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish-github-packages.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | - run: npm test 20 | 21 | publish-gpr: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: read 26 | packages: write 27 | steps: 28 | - uses: actions/checkout@v3 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version: 16 32 | registry-url: https://npm.pkg.github.com/ 33 | - run: npm ci 34 | - run: npm publish 35 | env: 36 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 37 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | - run: npm test 20 | 21 | publish-npm: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: 16 29 | registry-url: https://registry.npmjs.org/ 30 | - run: npm ci 31 | - run: npm publish 32 | env: 33 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 34 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues and pull requests 7 | 8 | on: 9 | schedule: 10 | - cron: '30 7 * * *' 11 | 12 | jobs: 13 | stale: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | pull-requests: write 19 | 20 | steps: 21 | - uses: actions/stale@v5 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | stale-issue-message: 'Stale issue message' 25 | stale-pr-message: 'Stale pull request message' 26 | stale-issue-label: 'no-issue-activity' 27 | stale-pr-label: 'no-pr-activity' 28 | -------------------------------------------------------------------------------- /.github/workflows/super-linter.yml: -------------------------------------------------------------------------------- 1 | # This workflow executes several linters on changed files based on languages used in your code base whenever 2 | # you push a code or open a pull request. 3 | # 4 | # You can adjust the behavior by modifying this file. 5 | # For more information, see: 6 | # https://github.com/github/super-linter 7 | name: Lint Code Base 8 | 9 | on: 10 | push: 11 | branches: [ "master" ] 12 | pull_request: 13 | branches: [ "master" ] 14 | jobs: 15 | run-lint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | with: 21 | # Full git history is needed to get a proper list of changed files within `super-linter` 22 | fetch-depth: 0 23 | 24 | - name: Lint Code Base 25 | uses: github/super-linter@v4 26 | env: 27 | VALIDATE_ALL_CODEBASE: false 28 | DEFAULT_BRANCH: "master" 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/tencent.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a docker container, publish and deploy it to Tencent Kubernetes Engine (TKE) when there is a push to the "master" branch. 2 | # 3 | # To configure this workflow: 4 | # 5 | # 1. Ensure that your repository contains the necessary configuration for your Tencent Kubernetes Engine cluster, 6 | # including deployment.yml, kustomization.yml, service.yml, etc. 7 | # 8 | # 2. Set up secrets in your workspace: 9 | # - TENCENT_CLOUD_SECRET_ID with Tencent Cloud secret id 10 | # - TENCENT_CLOUD_SECRET_KEY with Tencent Cloud secret key 11 | # - TENCENT_CLOUD_ACCOUNT_ID with Tencent Cloud account id 12 | # - TKE_REGISTRY_PASSWORD with TKE registry password 13 | # 14 | # 3. Change the values for the TKE_IMAGE_URL, TKE_REGION, TKE_CLUSTER_ID and DEPLOYMENT_NAME environment variables (below). 15 | 16 | name: Tencent Kubernetes Engine 17 | 18 | on: 19 | push: 20 | branches: [ "master" ] 21 | 22 | # Environment variables available to all jobs and steps in this workflow 23 | env: 24 | TKE_IMAGE_URL: ccr.ccs.tencentyun.com/demo/mywebapp 25 | TKE_REGION: ap-guangzhou 26 | TKE_CLUSTER_ID: cls-mywebapp 27 | DEPLOYMENT_NAME: tke-test 28 | 29 | permissions: 30 | contents: read 31 | 32 | jobs: 33 | setup-build-publish-deploy: 34 | name: Setup, Build, Publish, and Deploy 35 | runs-on: ubuntu-latest 36 | environment: production 37 | steps: 38 | 39 | - name: Checkout 40 | uses: actions/checkout@v3 41 | 42 | # Build 43 | - name: Build Docker image 44 | run: | 45 | docker build -t ${TKE_IMAGE_URL}:${GITHUB_SHA} . 46 | 47 | - name: Login TKE Registry 48 | run: | 49 | docker login -u ${{ secrets.TENCENT_CLOUD_ACCOUNT_ID }} -p '${{ secrets.TKE_REGISTRY_PASSWORD }}' ${TKE_IMAGE_URL} 50 | 51 | # Push the Docker image to TKE Registry 52 | - name: Publish 53 | run: | 54 | docker push ${TKE_IMAGE_URL}:${GITHUB_SHA} 55 | 56 | - name: Set up Kustomize 57 | run: | 58 | curl -o kustomize --location https://github.com/kubernetes-sigs/kustomize/releases/download/v3.1.0/kustomize_3.1.0_linux_amd64 59 | chmod u+x ./kustomize 60 | 61 | - name: Set up ~/.kube/config for connecting TKE cluster 62 | uses: TencentCloud/tke-cluster-credential-action@v1 63 | with: 64 | secret_id: ${{ secrets.TENCENT_CLOUD_SECRET_ID }} 65 | secret_key: ${{ secrets.TENCENT_CLOUD_SECRET_KEY }} 66 | tke_region: ${{ env.TKE_REGION }} 67 | cluster_id: ${{ env.TKE_CLUSTER_ID }} 68 | 69 | - name: Switch to TKE context 70 | run: | 71 | kubectl config use-context ${TKE_CLUSTER_ID}-context-default 72 | 73 | # Deploy the Docker image to the TKE cluster 74 | - name: Deploy 75 | run: | 76 | ./kustomize edit set image ${TKE_IMAGE_URL}:${GITHUB_SHA} 77 | ./kustomize build . | kubectl apply -f - 78 | kubectl rollout status deployment/${DEPLOYMENT_NAME} 79 | kubectl get services -o wide 80 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: "14.x" 15 | - run: npm install 16 | - run: npm run check-dependencies 17 | - run: npm test 18 | -------------------------------------------------------------------------------- /.github/workflows/webpack.yml: -------------------------------------------------------------------------------- 1 | name: NodeJS with Webpack 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [14.x, 16.x, 18.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Build 26 | run: | 27 | npm install 28 | npx webpack 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # General 61 | *.DS_Store 62 | .AppleDouble 63 | .LSOverride 64 | 65 | # Icon must end with two \r 66 | Icon 67 | 68 | 69 | # Thumbnails 70 | ._* 71 | 72 | # Files that might appear in the root of a volume 73 | .DocumentRevisions-V100 74 | .fseventsd 75 | .Spotlight-V100 76 | .TemporaryItems 77 | .Trashes 78 | .VolumeIcon.icns 79 | .com.apple.timemachine.donotpresent 80 | 81 | # Directories potentially created on remote AFP share 82 | .AppleDB 83 | .AppleDesktop 84 | Network Trash Folder 85 | Temporary Items 86 | .apdisk 87 | 88 | # JetBrains IDE project files 89 | /.idea 90 | 91 | TODO.md 92 | test.json 93 | spacex-api -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14.4.0 2 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff" 8 | }, 9 | "issueSettings": { 10 | "minSeverityLevel": "LOW" 11 | } 12 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM node:14-alpine 3 | 4 | LABEL maintainer="jakewmeyer@gmail.com" 5 | 6 | HEALTHCHECK --interval=10s --timeout=3s \ 7 | CMD ./scripts/healthcheck.js 8 | 9 | RUN apk add --no-cache --upgrade bash 10 | 11 | ENV NODE_ENV=production 12 | ENV HEALTH_URL=http://localhost:6673/v4/admin/health 13 | 14 | EXPOSE 6673 15 | 16 | # Run as an unprivileged user. 17 | RUN addgroup -S spacex && adduser -S -G spacex spacex 18 | RUN mkdir /app && chown spacex /app 19 | USER spacex 20 | 21 | WORKDIR /app 22 | ENTRYPOINT ["/app/start.sh"] 23 | 24 | COPY package.json package-lock.json /app/ 25 | 26 | RUN npm install --production 27 | 28 | COPY . . 29 | 30 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /SpaceXSpecs/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RocketList from './src/RocketList'; 3 | 4 | const App = () => { 5 | return ; 6 | }; 7 | 8 | export default App; 9 | -------------------------------------------------------------------------------- /SpaceXSpecs/RocketList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text, FlatList } from 'react-native'; 3 | import { getRocketSpecs } from './RocketSpecs'; 4 | 5 | const RocketList = () => { 6 | const [rockets, setRockets] = React.useState([]); 7 | 8 | React.useEffect(() => { 9 | const fetchData = async () => { 10 | const rocketData = await getRocketSpecs(); 11 | setRockets(rocketData); 12 | }; 13 | 14 | fetchData(); 15 | }, []); 16 | 17 | return ( 18 | 19 | item.id.toString()} 22 | renderItem={({ item }) => ( 23 | 24 | {item.name} 25 | {item.description} 26 | 27 | )} 28 | /> 29 | 30 | ); 31 | }; 32 | 33 | export default RocketList; 34 | -------------------------------------------------------------------------------- /SpaceXSpecs/RocketSpecs.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const getRocketSpecs = async () => { 4 | const response = await axios.get('https://api.spacexdata.com/v4/rockets'); 5 | return response.data; 6 | }; 7 | 8 | const getSpacecraftSpecs = async () => { 9 | const response = await axios.get('https://api.spacexdata.com/v4/spacecraft'); 10 | return response.data; 11 | }; 12 | 13 | export { getRocketSpecs, getSpacecraftSpecs }; 14 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const conditional = require('koa-conditional-get'); 2 | const etag = require('koa-etag'); 3 | const cors = require('koa2-cors'); 4 | const helmet = require('koa-helmet'); 5 | const Koa = require('koa'); 6 | const bodyParser = require('koa-bodyparser'); 7 | const mongoose = require('mongoose'); 8 | const { requestLogger, logger } = require('./middleware/logger'); 9 | const { responseTime, errors } = require('./middleware'); 10 | const { v4 } = require('./services'); 11 | 12 | const app = new Koa(); 13 | 14 | mongoose.connect(process.env.SPACEX_MONGO, { 15 | useFindAndModify: false, 16 | useNewUrlParser: true, 17 | useUnifiedTopology: true, 18 | useCreateIndex: true, 19 | }); 20 | 21 | const db = mongoose.connection; 22 | 23 | db.on('error', (err) => { 24 | logger.error(err); 25 | }); 26 | db.once('connected', () => { 27 | logger.info('Mongo connected'); 28 | app.emit('ready'); 29 | }); 30 | db.on('reconnected', () => { 31 | logger.info('Mongo re-connected'); 32 | }); 33 | db.on('disconnected', () => { 34 | logger.info('Mongo disconnected'); 35 | }); 36 | 37 | // disable console.errors for pino 38 | app.silent = true; 39 | 40 | // Error handler 41 | app.use(errors); 42 | 43 | app.use(conditional()); 44 | 45 | app.use(etag()); 46 | 47 | app.use(bodyParser()); 48 | 49 | // HTTP header security 50 | app.use(helmet()); 51 | 52 | // Enable CORS for all routes 53 | app.use(cors({ 54 | origin: '*', 55 | allowMethods: ['GET', 'POST', 'PATCH', 'DELETE'], 56 | allowHeaders: ['Content-Type', 'Accept'], 57 | exposeHeaders: ['spacex-api-cache', 'spacex-api-response-time'], 58 | })); 59 | 60 | // Set header with API response time 61 | app.use(responseTime); 62 | 63 | // Request logging 64 | app.use(requestLogger); 65 | 66 | // V4 routes 67 | app.use(v4.routes()); 68 | 69 | module.exports = app; 70 | -------------------------------------------------------------------------------- /docs/clients.md: -------------------------------------------------------------------------------- 1 | # List of known API clients / wrappers 2 | 3 | > _Do you, or do you know of some client/wrapper, that makes use of this community maintained service? If so, please [create an issue](https://github.com/r-spacex/SpaceX-API/issues/new) or [submit a PR](https://github.com/r-spacex/SpaceX-API/blob/master/CONTRIBUTING.md) with additions to this list. Thanks_ 4 | 5 | NOTE: Clients are grouped by API version(s) supported 6 | 7 | ### V4 (Latest) 8 | 9 | |Name|Lang|Creator(s)|Repo| 10 | |:---|:---|:---|:---| 11 | | Oddity | .NET | Tearth | [GitHub](https://github.com/Tearth/Oddity) | 12 | | SpaceX-Async-Wrapper | Python | Ryu JuHeon | [GitHub](https://github.com/SaidBySolo/SpaceX-Async-Wrapper) | 13 | | KSBSpacexKit | Swift | SaiBalaji22 | [GitHub](https://github.com/SaiBalaji22/KSBSpacexKit) | 14 | | Marsy | C++ | AzuxDario | [GitHub](https://github.com/AzuxDario/Marsy) | 15 | 16 | ### V3, V2, V1 (Deprecated) 17 | 18 | |Name|Lang|Creator(s)|Repo| 19 | |:---|:---|:---|:---| 20 | | SpaceX-GraphQL | TypeScript | Jordan Owens | [GitHub](https://github.com/jor-dan/SpaceX-GraphQL) | 21 | | spacex-graphql-api | GraphQL | Emerson Laurentino | [GitHub](https://github.com/emersonlaurentino/spacex-qraphql-api) | 22 | | SpaceX-API-Wrapper | Node.js | Thomas Smyth | [GitHub](https://github.com/Thomas-Smyth/SpaceX-API-Wrapper) | 23 | | spacex-api | TypeScript / Node.js | Tomasz Borowski | [GitHub](https://github.com/tbprojects/spacex-api), [NPM](https://www.npmjs.com/package/spacex-api) | 24 | | SpaceX | PowerShell | François-Xavier Cat | [GitHub](https://github.com/lazywinadmin/SpaceX) | 25 | | SpacePY-X | Python | Andrew Shapton | [GitHub](https://github.com/alshapton/SpacePY-X) | 26 | | SpaceX-PY | Python | Kaylum Lally | [GitHub](https://github.com/HiKaylum/SpaceX-PY) | 27 | | SpaceNIM-X | Nim | Andrew Shapton | [GitHub](https://github.com/alshapton/SpaceNIM-X) | 28 | | SpaceCRYST-X | Crystal | Andrew Shapton | [GitHub](https://github.com/alshapton/SpaceCRYST-X) | 29 | | spacex | Go | Or Hiltch | [GitHub](https://github.com/orcaman/spacex) | 30 | | spacex-api-wrapper | Rust | Alex Gutan | [GitHub](https://github.com/AGutan/spacex-api-wrapper)| 31 | | spacex-graphql-rust | Rust | Aaron Feigenbaum | [GitHub](https://github.com/adace123/spacex-graphql-rust)| 32 | | space-rx | Rust | Tyler Wilcock | [GitHub](https://github.com/twilco/space-rx) | 33 | | spacex | Ruby | Rodolfo | [GitHub](https://github.com/rodolfobandeira/spacex) | 34 | | spacex | PHP | Aires Gonçalves | [GitHub](https://github.com/airesvsg/spacex) | 35 | | spacex_ex | Elixir | Chen Zhao | [GitHub](https://github.com/crunchysoul/spacex_ex) | 36 | | SpaceX | R | Johannes Friedrich | [GitHub](https://github.com/JohannesFriedrich/SpaceX) | 37 | | SpaceXAPI-Swift | Swift | Sami Sharafeddine | [GitHub](https://github.com/devsamsh/SpaceXAPI-Swift) | 38 | -------------------------------------------------------------------------------- /docs/v4/capsules/all.md: -------------------------------------------------------------------------------- 1 | # Get all capsules 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/capsules` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "reuse_count": 1, 17 | "water_landings": 1, 18 | "land_landings": 0, 19 | "last_update": "Reentered after three weeks in orbit", 20 | "launches": [ 21 | "5eb87cdeffd86e000604b330" 22 | ], 23 | "serial": "C101", 24 | "status": "retired", 25 | "id": "5e9e2c5bf35918ed873b2664" 26 | }, 27 | ... 28 | ] 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/v4/capsules/create.md: -------------------------------------------------------------------------------- 1 | # Create a capsule 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/capsules` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "reuse_count": 1, 14 | "water_landings": 1, 15 | "land_landings": 0, 16 | "last_update": "Reentered after three weeks in orbit", 17 | "launches": [ 18 | "5eb87cdeffd86e000604b330" 19 | ], 20 | "serial": "C101", 21 | "status": "retired" 22 | } 23 | ``` 24 | 25 | ## Success Response 26 | 27 | **Code** : `201 Created` 28 | 29 | **Content example** : `Created` 30 | 31 | ## Error Responses 32 | 33 | **Code** : `400 Bad Request` 34 | 35 | **Content** : Mongoose error is shown, with suggestions to fix the query. 36 | -------------------------------------------------------------------------------- /docs/v4/capsules/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a capsule 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/capsules/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the capsule 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/capsules/one.md: -------------------------------------------------------------------------------- 1 | # Get one capsule 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/capsules/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the capsule 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "reuse_count": 1, 20 | "water_landings": 1, 21 | "land_landings": 0, 22 | "last_update": "Reentered after three weeks in orbit", 23 | "launches": [ 24 | "5eb87cdeffd86e000604b330" 25 | ], 26 | "serial": "C101", 27 | "status": "retired", 28 | "id": "5e9e2c5bf35918ed873b2664" 29 | } 30 | ``` 31 | 32 | ## Error Responses 33 | 34 | **Code** : `404 NOT FOUND` 35 | 36 | **Content** : `Not Found` 37 | -------------------------------------------------------------------------------- /docs/v4/capsules/query.md: -------------------------------------------------------------------------------- 1 | # Query capsules 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/capsules/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "reuse_count": 1, 31 | "water_landings": 1, 32 | "land_landings": 0, 33 | "last_update": "Reentered after three weeks in orbit", 34 | "launches": [ 35 | "5eb87cdeffd86e000604b330" 36 | ], 37 | "serial": "C101", 38 | "status": "retired", 39 | "id": "5e9e2c5bf35918ed873b2664" 40 | }, 41 | ... 42 | ], 43 | "totalDocs": 19, 44 | "offset": 0, 45 | "limit": 10, 46 | "totalPages": 2, 47 | "page": 1, 48 | "pagingCounter": 1, 49 | "hasPrevPage": false, 50 | "hasNextPage": true, 51 | "prevPage": null, 52 | "nextPage": 2 53 | } 54 | ``` 55 | 56 | ## Error Responses 57 | 58 | **Code** : `400 Bad Request` 59 | 60 | **Content** : Mongoose error is shown, with suggestions to fix the query. 61 | -------------------------------------------------------------------------------- /docs/v4/capsules/schema.md: -------------------------------------------------------------------------------- 1 | # Capsule Schema 2 | 3 | ```json 4 | { 5 | "serial": { 6 | "type": "String", 7 | "required": true, 8 | "unique": true, 9 | }, 10 | "status": { 11 | "type": "String", 12 | "enum": ["unknown", "active", "retired", "destroyed"], 13 | "required": true, 14 | }, 15 | "dragon": { 16 | "type": "UUID", 17 | }, 18 | "reuse_count": { 19 | "type": "Number", 20 | "default": 0, 21 | }, 22 | "water_landings": { 23 | "type": "Number", 24 | "default": 0, 25 | }, 26 | "land_landings": { 27 | "type": "Number", 28 | "default": 0, 29 | }, 30 | "last_update": { 31 | "type": "String", 32 | "default": null, 33 | }, 34 | "launches": [{ 35 | "type": "UUID", 36 | }], 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/v4/capsules/update.md: -------------------------------------------------------------------------------- 1 | # Update a capsule 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/capsules/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the capsule 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "status": "retired", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/company/all.md: -------------------------------------------------------------------------------- 1 | # Get all company info 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/company` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | { 15 | "headquarters": { 16 | "address": "Rocket Road", 17 | "city": "Hawthorne", 18 | "state": "California" 19 | }, 20 | "links": { 21 | "website": "https://www.spacex.com/", 22 | "flickr": "https://www.flickr.com/photos/spacex/", 23 | "twitter": "https://twitter.com/SpaceX", 24 | "elon_twitter": "https://twitter.com/elonmusk" 25 | }, 26 | "name": "SpaceX", 27 | "founder": "Elon Musk", 28 | "founded": 2002, 29 | "employees": 8000, 30 | "vehicles": 3, 31 | "launch_sites": 3, 32 | "test_sites": 1, 33 | "ceo": "Elon Musk", 34 | "cto": "Elon Musk", 35 | "coo": "Gwynne Shotwell", 36 | "cto_propulsion": "Tom Mueller", 37 | "valuation": 52000000000, 38 | "summary": "SpaceX designs, manufactures and launches advanced rockets and spacecraft. The company was founded in 2002 to revolutionize space technology, with the ultimate goal of enabling people to live on other planets.", 39 | "id": "5eb75edc42fea42237d7f3ed" 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/v4/company/schema.md: -------------------------------------------------------------------------------- 1 | # Company Info Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String" 7 | }, 8 | "founder": { 9 | "type": "String" 10 | }, 11 | "founded": { 12 | "type": "Number" 13 | }, 14 | "employees": { 15 | "type": "Number" 16 | }, 17 | "vehicles": { 18 | "type": "Number" 19 | }, 20 | "launch_sites": { 21 | "type": "Number" 22 | }, 23 | "test_sites": { 24 | "type": "Number" 25 | }, 26 | "ceo": { 27 | "type": "String" 28 | }, 29 | "cto": { 30 | "type": "String" 31 | }, 32 | "coo": { 33 | "type": "String" 34 | }, 35 | "cto_propulsion": { 36 | "type": "String" 37 | }, 38 | "valuation": { 39 | "type": "Number" 40 | }, 41 | "headquarters": { 42 | "address": { 43 | "type": "String" 44 | }, 45 | "city": { 46 | "type": "String" 47 | }, 48 | "state": { 49 | "type": "String" 50 | } 51 | }, 52 | "links": { 53 | "website": { 54 | "type": "String" 55 | }, 56 | "flickr": { 57 | "type": "String" 58 | }, 59 | "twitter": { 60 | "type": "String" 61 | }, 62 | "elon_twitter": { 63 | "type": "String" 64 | } 65 | }, 66 | "summary": { 67 | "type": "String" 68 | } 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/v4/company/update.md: -------------------------------------------------------------------------------- 1 | # Update company info 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/company/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the company 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "employees": 8000 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/cores/all.md: -------------------------------------------------------------------------------- 1 | # Get all cores 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "block": 5, 17 | "reuse_count": 3, 18 | "rtls_attempts": 1, 19 | "rtls_landings": 1, 20 | "asds_attempts": 3, 21 | "asds_landings": 3, 22 | "last_update": "Landed on OCISLY as of Jan 29, 2020. ", 23 | "launches": [ 24 | "5eb87d2bffd86e000604b375", 25 | "5eb87d31ffd86e000604b379", 26 | "5eb87d3fffd86e000604b382", 27 | "5eb87d44ffd86e000604b386" 28 | ], 29 | "serial": "B1051", 30 | "status": "active", 31 | "id": "5e9e28a6f35918c0803b265c" 32 | }, 33 | ... 34 | ] 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/v4/cores/create.md: -------------------------------------------------------------------------------- 1 | # Create a core 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "block": 5, 14 | "reuse_count": 3, 15 | "rtls_attempts": 1, 16 | "rtls_landings": 1, 17 | "asds_attempts": 3, 18 | "asds_landings": 3, 19 | "last_update": "Landed on OCISLY as of Jan 29, 2020. ", 20 | "launches": [ 21 | "5eb87d2bffd86e000604b375", 22 | "5eb87d31ffd86e000604b379", 23 | "5eb87d3fffd86e000604b382", 24 | "5eb87d44ffd86e000604b386" 25 | ], 26 | "serial": "B1051", 27 | "status": "active" 28 | } 29 | ``` 30 | 31 | ## Success Response 32 | 33 | **Code** : `201 Created` 34 | 35 | **Content example** : `Created` 36 | 37 | ## Error Responses 38 | 39 | **Code** : `400 Bad Request` 40 | 41 | **Content** : Mongoose error is shown, with suggestions to fix the query. 42 | -------------------------------------------------------------------------------- /docs/v4/cores/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a core 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the core 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/cores/one.md: -------------------------------------------------------------------------------- 1 | # Get one core 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the core 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "block": 5, 20 | "reuse_count": 3, 21 | "rtls_attempts": 1, 22 | "rtls_landings": 1, 23 | "asds_attempts": 3, 24 | "asds_landings": 3, 25 | "last_update": "Landed on OCISLY as of Jan 29, 2020. ", 26 | "launches": [ 27 | "5eb87d2bffd86e000604b375", 28 | "5eb87d31ffd86e000604b379", 29 | "5eb87d3fffd86e000604b382", 30 | "5eb87d44ffd86e000604b386" 31 | ], 32 | "serial": "B1051", 33 | "status": "active", 34 | "id": "5e9e28a6f35918c0803b265c" 35 | } 36 | ``` 37 | 38 | ## Error Responses 39 | 40 | **Code** : `404 NOT FOUND` 41 | 42 | **Content** : `Not Found` 43 | -------------------------------------------------------------------------------- /docs/v4/cores/query.md: -------------------------------------------------------------------------------- 1 | # Query cores 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "block": 5, 31 | "reuse_count": 3, 32 | "rtls_attempts": 1, 33 | "rtls_landings": 1, 34 | "asds_attempts": 2, 35 | "asds_landings": 2, 36 | "last_update": "Missed the droneship and made successful water landing; apparently scuttled at sea afterward. ", 37 | "launches": [ 38 | "5eb87d2effd86e000604b377", 39 | "5eb87d36ffd86e000604b37b", 40 | "5eb87d3bffd86e000604b37f", 41 | "5eb87d41ffd86e000604b383" 42 | ], 43 | "serial": "B1056", 44 | "status": "lost", 45 | "id": "5e9e28a7f3591809313b2660" 46 | }, 47 | ... 48 | ], 49 | "totalDocs": 65, 50 | "offset": 0, 51 | "limit": 10, 52 | "totalPages": 7, 53 | "page": 1, 54 | "pagingCounter": 1, 55 | "hasPrevPage": false, 56 | "hasNextPage": true, 57 | "prevPage": null, 58 | "nextPage": 2 59 | } 60 | ``` 61 | 62 | ## Error Responses 63 | 64 | **Code** : `400 Bad Request` 65 | 66 | **Content** : Mongoose error is shown, with suggestions to fix the query. 67 | -------------------------------------------------------------------------------- /docs/v4/cores/schema.md: -------------------------------------------------------------------------------- 1 | # Core Schema 2 | 3 | ```json 4 | { 5 | "serial": { 6 | "type": "String", 7 | "unique": true, 8 | "required": true, 9 | }, 10 | "block": { 11 | "type": "Number", 12 | "default": null, 13 | }, 14 | "status": { 15 | "type": "String", 16 | "enum": ["active", "inactive", "unknown", "expended", "lost", "retired"], 17 | "required": true, 18 | }, 19 | "reuse_count": { 20 | "type": "Number", 21 | "default": 0, 22 | }, 23 | "rtls_attempts": { 24 | "type": "Number", 25 | "default": 0, 26 | }, 27 | "rtls_landings": { 28 | "type": "Number", 29 | "default": 0, 30 | }, 31 | "asds_attempts": { 32 | "type": "Number", 33 | "default": 0, 34 | }, 35 | "asds_landings": { 36 | "type": "Number", 37 | "default": 0, 38 | }, 39 | "last_update": { 40 | "type": "String", 41 | "default": null, 42 | }, 43 | "launches": [{ 44 | "type": "UUID", 45 | }], 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/v4/cores/update.md: -------------------------------------------------------------------------------- 1 | # Update a core 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the capsule 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "status": "expended", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/crew/all.md: -------------------------------------------------------------------------------- 1 | # Get all crew 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/crew` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "name": "Robert Behnken", 17 | "agency": "NASA", 18 | "image": "https://imgur.com/0smMgMH.png", 19 | "wikipedia": "https://en.wikipedia.org/wiki/Robert_L._Behnken", 20 | "launches": [ 21 | "5eb87d46ffd86e000604b388" 22 | ], 23 | "status": "active", 24 | "id": "5ebf1a6e23a9a60006e03a7a" 25 | }, 26 | ... 27 | ] 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/v4/crew/create.md: -------------------------------------------------------------------------------- 1 | # Create a crew member 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/crew` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "name": "Douglas Hurley", 14 | "agency": "NASA", 15 | "image": "https://i.imgur.com/ooaayWf.png", 16 | "wikipedia": "https://en.wikipedia.org/wiki/Douglas_G._Hurley", 17 | "launches": [ 18 | "5eb87d46ffd86e000604b388" 19 | ], 20 | "status": "active", 21 | "id": "5ebf1b7323a9a60006e03a7b" 22 | } 23 | ``` 24 | 25 | ## Success Response 26 | 27 | **Code** : `201 Created` 28 | 29 | **Content example** : `Created` 30 | 31 | ## Error Responses 32 | 33 | **Code** : `400 Bad Request` 34 | 35 | **Content** : Mongoose error is shown, with suggestions to fix the query. 36 | -------------------------------------------------------------------------------- /docs/v4/crew/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a crew member 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/crew/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the crew member 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/crew/one.md: -------------------------------------------------------------------------------- 1 | # Get one crew member 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/crew/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the crew member 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "name": "Douglas Hurley", 20 | "agency": "NASA", 21 | "image": "https://i.imgur.com/ooaayWf.png", 22 | "wikipedia": "https://en.wikipedia.org/wiki/Douglas_G._Hurley", 23 | "launches": [ 24 | "5eb87d46ffd86e000604b388" 25 | ], 26 | "status": "active", 27 | "id": "5ebf1b7323a9a60006e03a7b" 28 | } 29 | ``` 30 | 31 | ## Error Responses 32 | 33 | **Code** : `404 NOT FOUND` 34 | 35 | **Content** : `Not Found` 36 | -------------------------------------------------------------------------------- /docs/v4/crew/query.md: -------------------------------------------------------------------------------- 1 | # Query crew members 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/crew/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "name": "Robert Behnken", 31 | "agency": "NASA", 32 | "image": "https://imgur.com/0smMgMH.png", 33 | "wikipedia": "https://en.wikipedia.org/wiki/Robert_L._Behnken", 34 | "launches": [ 35 | "5eb87d46ffd86e000604b388" 36 | ], 37 | "status": "active", 38 | "id": "5ebf1a6e23a9a60006e03a7a" 39 | }, 40 | ... 41 | ], 42 | "totalDocs": 2, 43 | "offset": 0, 44 | "limit": 10, 45 | "totalPages": 1, 46 | "page": 1, 47 | "pagingCounter": 1, 48 | "hasPrevPage": false, 49 | "hasNextPage": false, 50 | "prevPage": null, 51 | "nextPage": null 52 | } 53 | ``` 54 | 55 | ## Error Responses 56 | 57 | **Code** : `400 Bad Request` 58 | 59 | **Content** : Mongoose error is shown, with suggestions to fix the query. 60 | -------------------------------------------------------------------------------- /docs/v4/crew/schema.md: -------------------------------------------------------------------------------- 1 | # Crew Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String", 7 | "default": null 8 | }, 9 | "status": { 10 | "type": "String", 11 | "required": true, 12 | "enum": ["active", "inactive", "retired", "unknown"] 13 | }, 14 | "agency": { 15 | "type": "String", 16 | "default": null 17 | }, 18 | "image": { 19 | "type": "String", 20 | "default": null 21 | }, 22 | "wikipedia": { 23 | "type": "String", 24 | "default": null 25 | }, 26 | "launches": [{ 27 | "type": "UUID" 28 | }] 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/v4/crew/update.md: -------------------------------------------------------------------------------- 1 | # Update a crew member 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/crew/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the crew member 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "agency": "NASA", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/dragons/all.md: -------------------------------------------------------------------------------- 1 | # Get all Dragons 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/dragons` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "heat_shield": { 17 | "material": "PICA-X", 18 | "size_meters": 3.6, 19 | "temp_degrees": 3000, 20 | "dev_partner": "NASA" 21 | }, 22 | "launch_payload_mass": { 23 | "kg": 6000, 24 | "lb": 13228 25 | }, 26 | "launch_payload_vol": { 27 | "cubic_meters": 25, 28 | "cubic_feet": 883 29 | }, 30 | "return_payload_mass": { 31 | "kg": 3000, 32 | "lb": 6614 33 | }, 34 | "return_payload_vol": { 35 | "cubic_meters": 11, 36 | "cubic_feet": 388 37 | }, 38 | "pressurized_capsule": { 39 | "payload_volume": { 40 | "cubic_meters": 11, 41 | "cubic_feet": 388 42 | } 43 | }, 44 | "trunk": { 45 | "trunk_volume": { 46 | "cubic_meters": 14, 47 | "cubic_feet": 494 48 | }, 49 | "cargo": { 50 | "solar_array": 2, 51 | "unpressurized_cargo": true 52 | } 53 | }, 54 | "height_w_trunk": { 55 | "meters": 7.2, 56 | "feet": 23.6 57 | }, 58 | "diameter": { 59 | "meters": 3.7, 60 | "feet": 12 61 | }, 62 | "first_flight": "2010-12-08", 63 | "flickr_images": [ 64 | "https://www.spacex.com/sites/spacex/files/styles/media_gallery_large/public/2015_-_04_crs5_dragon_orbit13.jpg?itok=9p8_l7UP", 65 | "https://www.spacex.com/sites/spacex/files/styles/media_gallery_large/public/2012_-_4_dragon_grapple_cots2-1.jpg?itok=R2-SeuMX", 66 | "https://farm3.staticflickr.com/2815/32761844973_4b55b27d3c_b.jpg", 67 | "https://farm9.staticflickr.com/8618/16649075267_d18cbb4342_b.jpg" 68 | ], 69 | "name": "Dragon 1", 70 | "type": "capsule", 71 | "active": true, 72 | "crew_capacity": 0, 73 | "sidewall_angle_deg": 15, 74 | "orbit_duration_yr": 2, 75 | "dry_mass_kg": 4200, 76 | "dry_mass_lb": 9300, 77 | "thrusters": [ 78 | { 79 | "type": "Draco", 80 | "amount": 18, 81 | "pods": 4, 82 | "fuel_1": "nitrogen tetroxide", 83 | "fuel_2": "monomethylhydrazine", 84 | "isp": 300, 85 | "thrust": { 86 | "kN": 0.4, 87 | "lbf": 90 88 | } 89 | } 90 | ], 91 | "wikipedia": "https://en.wikipedia.org/wiki/SpaceX_Dragon", 92 | "description": "Dragon is a reusable spacecraft developed by SpaceX, an American private space transportation company based in Hawthorne, California. Dragon is launched into space by the SpaceX Falcon 9 two-stage-to-orbit launch vehicle. The Dragon spacecraft was originally designed for human travel, but so far has only been used to deliver cargo to the International Space Station (ISS).", 93 | "id": "5e9d058759b1ff74a7ad5f8f" 94 | }, 95 | ... 96 | ] 97 | ``` 98 | -------------------------------------------------------------------------------- /docs/v4/dragons/create.md: -------------------------------------------------------------------------------- 1 | # Create a Dragon 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/dragons` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "heat_shield": { 14 | "material": "PICA-X", 15 | "size_meters": 3.6, 16 | "temp_degrees": 3000, 17 | "dev_partner": "NASA" 18 | }, 19 | "launch_payload_mass": { 20 | "kg": 6000, 21 | "lb": 13228 22 | }, 23 | "launch_payload_vol": { 24 | "cubic_meters": 25, 25 | "cubic_feet": 883 26 | }, 27 | "return_payload_mass": { 28 | "kg": 3000, 29 | "lb": 6614 30 | }, 31 | "return_payload_vol": { 32 | "cubic_meters": 11, 33 | "cubic_feet": 388 34 | }, 35 | "pressurized_capsule": { 36 | "payload_volume": { 37 | "cubic_meters": 11, 38 | "cubic_feet": 388 39 | } 40 | }, 41 | "trunk": { 42 | "trunk_volume": { 43 | "cubic_meters": 14, 44 | "cubic_feet": 494 45 | }, 46 | "cargo": { 47 | "solar_array": 2, 48 | "unpressurized_cargo": true 49 | } 50 | }, 51 | "height_w_trunk": { 52 | "meters": 7.2, 53 | "feet": 23.6 54 | }, 55 | "diameter": { 56 | "meters": 3.7, 57 | "feet": 12 58 | }, 59 | "first_flight": "2010-12-08", 60 | "flickr_images": [ 61 | "https://www.spacex.com/sites/spacex/files/styles/media_gallery_large/public/2015_-_04_crs5_dragon_orbit13.jpg?itok=9p8_l7UP", 62 | "https://www.spacex.com/sites/spacex/files/styles/media_gallery_large/public/2012_-_4_dragon_grapple_cots2-1.jpg?itok=R2-SeuMX", 63 | "https://farm3.staticflickr.com/2815/32761844973_4b55b27d3c_b.jpg", 64 | "https://farm9.staticflickr.com/8618/16649075267_d18cbb4342_b.jpg" 65 | ], 66 | "name": "Dragon 1", 67 | "type": "capsule", 68 | "active": true, 69 | "crew_capacity": 0, 70 | "sidewall_angle_deg": 15, 71 | "orbit_duration_yr": 2, 72 | "dry_mass_kg": 4200, 73 | "dry_mass_lb": 9300, 74 | "thrusters": [ 75 | { 76 | "type": "Draco", 77 | "amount": 18, 78 | "pods": 4, 79 | "fuel_1": "nitrogen tetroxide", 80 | "fuel_2": "monomethylhydrazine", 81 | "isp": 300, 82 | "thrust": { 83 | "kN": 0.4, 84 | "lbf": 90 85 | } 86 | } 87 | ], 88 | "wikipedia": "https://en.wikipedia.org/wiki/SpaceX_Dragon", 89 | "description": "Dragon is a reusable spacecraft developed by SpaceX, an American private space transportation company based in Hawthorne, California. Dragon is launched into space by the SpaceX Falcon 9 two-stage-to-orbit launch vehicle. The Dragon spacecraft was originally designed for human travel, but so far has only been used to deliver cargo to the International Space Station (ISS).", 90 | "id": "5e9d058759b1ff74a7ad5f8f" 91 | } 92 | ``` 93 | 94 | ## Success Response 95 | 96 | **Code** : `201 Created` 97 | 98 | **Content example** : `Created` 99 | 100 | ## Error Responses 101 | 102 | **Code** : `400 Bad Request` 103 | 104 | **Content** : Mongoose error is shown, with suggestions to fix the query. 105 | -------------------------------------------------------------------------------- /docs/v4/dragons/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a Dragon 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/dragons/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the dragon 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/dragons/one.md: -------------------------------------------------------------------------------- 1 | # Get one Dragon 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/dragons/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the dragon 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "heat_shield": { 20 | "material": "PICA-X", 21 | "size_meters": 3.6, 22 | "temp_degrees": 3000, 23 | "dev_partner": "NASA" 24 | }, 25 | "launch_payload_mass": { 26 | "kg": 6000, 27 | "lb": 13228 28 | }, 29 | "launch_payload_vol": { 30 | "cubic_meters": 25, 31 | "cubic_feet": 883 32 | }, 33 | "return_payload_mass": { 34 | "kg": 3000, 35 | "lb": 6614 36 | }, 37 | "return_payload_vol": { 38 | "cubic_meters": 11, 39 | "cubic_feet": 388 40 | }, 41 | "pressurized_capsule": { 42 | "payload_volume": { 43 | "cubic_meters": 11, 44 | "cubic_feet": 388 45 | } 46 | }, 47 | "trunk": { 48 | "trunk_volume": { 49 | "cubic_meters": 14, 50 | "cubic_feet": 494 51 | }, 52 | "cargo": { 53 | "solar_array": 2, 54 | "unpressurized_cargo": true 55 | } 56 | }, 57 | "height_w_trunk": { 58 | "meters": 7.2, 59 | "feet": 23.6 60 | }, 61 | "diameter": { 62 | "meters": 3.7, 63 | "feet": 12 64 | }, 65 | "first_flight": "2010-12-08", 66 | "flickr_images": [ 67 | "https://www.spacex.com/sites/spacex/files/styles/media_gallery_large/public/2015_-_04_crs5_dragon_orbit13.jpg?itok=9p8_l7UP", 68 | "https://www.spacex.com/sites/spacex/files/styles/media_gallery_large/public/2012_-_4_dragon_grapple_cots2-1.jpg?itok=R2-SeuMX", 69 | "https://farm3.staticflickr.com/2815/32761844973_4b55b27d3c_b.jpg", 70 | "https://farm9.staticflickr.com/8618/16649075267_d18cbb4342_b.jpg" 71 | ], 72 | "name": "Dragon 1", 73 | "type": "capsule", 74 | "active": true, 75 | "crew_capacity": 0, 76 | "sidewall_angle_deg": 15, 77 | "orbit_duration_yr": 2, 78 | "dry_mass_kg": 4200, 79 | "dry_mass_lb": 9300, 80 | "thrusters": [ 81 | { 82 | "type": "Draco", 83 | "amount": 18, 84 | "pods": 4, 85 | "fuel_1": "nitrogen tetroxide", 86 | "fuel_2": "monomethylhydrazine", 87 | "isp": 300, 88 | "thrust": { 89 | "kN": 0.4, 90 | "lbf": 90 91 | } 92 | } 93 | ], 94 | "wikipedia": "https://en.wikipedia.org/wiki/SpaceX_Dragon", 95 | "description": "Dragon is a reusable spacecraft developed by SpaceX, an American private space transportation company based in Hawthorne, California. Dragon is launched into space by the SpaceX Falcon 9 two-stage-to-orbit launch vehicle. The Dragon spacecraft was originally designed for human travel, but so far has only been used to deliver cargo to the International Space Station (ISS).", 96 | "id": "5e9d058759b1ff74a7ad5f8f" 97 | } 98 | ``` 99 | 100 | ## Error Responses 101 | 102 | **Code** : `404 NOT FOUND` 103 | 104 | **Content** : `Not Found` 105 | -------------------------------------------------------------------------------- /docs/v4/dragons/schema.md: -------------------------------------------------------------------------------- 1 | # Dragon Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String", 7 | "unique": true, 8 | "required": true 9 | }, 10 | "type": { 11 | "type": "String", 12 | "required": true 13 | }, 14 | "active": { 15 | "type": "Boolean", 16 | "required": true 17 | }, 18 | "crew_capacity": { 19 | "type": "Number", 20 | "required": true 21 | }, 22 | "sidewall_angle_deg": { 23 | "type": "Number", 24 | "required": true 25 | }, 26 | "orbit_duration_yr": { 27 | "type": "Number", 28 | "required": true 29 | }, 30 | "dry_mass_kg": { 31 | "type": "Number", 32 | "required": true 33 | }, 34 | "dry_mass_lb": { 35 | "type": "Number", 36 | "required": true 37 | }, 38 | "first_flight": { 39 | "type": "String", 40 | "default": null 41 | }, 42 | "heat_shield": { 43 | "material": { 44 | "type": "String", 45 | "required": true 46 | }, 47 | "size_meters": { 48 | "type": "Number", 49 | "required": true 50 | }, 51 | "temp_degrees": { 52 | "type": "Number" 53 | }, 54 | "dev_partner": { 55 | "type": "String" 56 | } 57 | }, 58 | "thrusters": { 59 | "type": "Object" 60 | }, 61 | "launch_payload_mass": { 62 | "kg": { 63 | "type": "Number" 64 | }, 65 | "lb": { 66 | "type": "Number" 67 | } 68 | }, 69 | "launch_payload_vol": { 70 | "cubic_meters": { 71 | "type": "Number" 72 | }, 73 | "cubic_feet": { 74 | "type": "Number" 75 | } 76 | }, 77 | "return_payload_mass": { 78 | "kg": { 79 | "type": "Number" 80 | }, 81 | "lb": { 82 | "type": "Number" 83 | } 84 | }, 85 | "return_payload_vol": { 86 | "cubic_meters": { 87 | "type": "Number" 88 | }, 89 | "cubic_feet": { 90 | "type": "Number" 91 | } 92 | }, 93 | "pressurized_capsule": { 94 | "payload_volume": { 95 | "cubic_meters": { 96 | "type": "Number" 97 | }, 98 | "cubic_feet": { 99 | "type": "Number" 100 | } 101 | } 102 | }, 103 | "trunk": { 104 | "trunk_volume": { 105 | "cubic_meters": { 106 | "type": "Number" 107 | }, 108 | "cubic_feet": { 109 | "type": "Number" 110 | } 111 | }, 112 | "cargo": { 113 | "solar_array": { 114 | "type": "Number" 115 | }, 116 | "unpressurized_cargo": { 117 | "type": "Boolean" 118 | } 119 | } 120 | }, 121 | "height_w_trunk": { 122 | "meters": { 123 | "type": "Number" 124 | }, 125 | "feet": { 126 | "type": "Number" 127 | } 128 | }, 129 | "diameter": { 130 | "meters": { 131 | "type": "Number" 132 | }, 133 | "feet": { 134 | "type": "Number" 135 | } 136 | }, 137 | "flickr_images": { 138 | "type": [ 139 | "String" 140 | ] 141 | }, 142 | "wikipedia": { 143 | "type": "String" 144 | }, 145 | "description": { 146 | "type": "String" 147 | } 148 | } 149 | ``` 150 | -------------------------------------------------------------------------------- /docs/v4/dragons/update.md: -------------------------------------------------------------------------------- 1 | # Update a Dragon 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/dragons/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the Dragon 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "sidewall_angle_deg": 15, 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/landpads/all.md: -------------------------------------------------------------------------------- 1 | # Get all landing pads 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/landpads` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "name": "LZ-2", 17 | "full_name": "Landing Zone 2", 18 | "status": "active", 19 | "type": "RTLS", 20 | "locality": "Cape Canaveral", 21 | "region": "Florida", 22 | "latitude": 28.485833, 23 | "longitude": -80.544444, 24 | "landing_attempts": 3, 25 | "landing_successes": 3, 26 | "wikipedia": "https://en.wikipedia.org/wiki/Landing_Zones_1_and_2", 27 | "details": "SpaceX's first east coast landing pad is Landing Zone 1, where the historic first Falcon 9 landing occurred in December 2015. LC-13 was originally used as a launch pad for early Atlas missiles and rockets from Lockheed Martin. LC-1 was later expanded to include Landing Zone 2 for side booster RTLS Falcon Heavy missions, and it was first used in February 2018 for that purpose.", 28 | "launches": [ 29 | "5eb87d13ffd86e000604b360", 30 | "5eb87d2dffd86e000604b376", 31 | "5eb87d35ffd86e000604b37a" 32 | ], 33 | "id": "5e9e3032383ecb90a834e7c8" 34 | }, 35 | ... 36 | ] 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/v4/landpads/create.md: -------------------------------------------------------------------------------- 1 | # Create a landing pad 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/landpads` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "name": "LZ-2", 14 | "full_name": "Landing Zone 2", 15 | "status": "active", 16 | "type": "RTLS", 17 | "locality": "Cape Canaveral", 18 | "region": "Florida", 19 | "latitude": 28.485833, 20 | "longitude": -80.544444, 21 | "landing_attempts": 3, 22 | "landing_successes": 3, 23 | "wikipedia": "https://en.wikipedia.org/wiki/Landing_Zones_1_and_2", 24 | "details": "SpaceX's first east coast landing pad is Landing Zone 1, where the historic first Falcon 9 landing occurred in December 2015. LC-13 was originally used as a launch pad for early Atlas missiles and rockets from Lockheed Martin. LC-1 was later expanded to include Landing Zone 2 for side booster RTLS Falcon Heavy missions, and it was first used in February 2018 for that purpose.", 25 | "launches": [ 26 | "5eb87d13ffd86e000604b360", 27 | "5eb87d2dffd86e000604b376", 28 | "5eb87d35ffd86e000604b37a" 29 | ] 30 | } 31 | ``` 32 | 33 | ## Success Response 34 | 35 | **Code** : `201 Created` 36 | 37 | **Content example** : `Created` 38 | 39 | ## Error Responses 40 | 41 | **Code** : `400 Bad Request` 42 | 43 | **Content** : Mongoose error is shown, with suggestions to fix the query. 44 | -------------------------------------------------------------------------------- /docs/v4/landpads/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a landing pad 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the core 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/landpads/one.md: -------------------------------------------------------------------------------- 1 | # Get one landing pad 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/landpads/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the landing pad 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "name": "LZ-2", 20 | "full_name": "Landing Zone 2", 21 | "status": "active", 22 | "type": "RTLS", 23 | "locality": "Cape Canaveral", 24 | "region": "Florida", 25 | "latitude": 28.485833, 26 | "longitude": -80.544444, 27 | "landing_attempts": 3, 28 | "landing_successes": 3, 29 | "wikipedia": "https://en.wikipedia.org/wiki/Landing_Zones_1_and_2", 30 | "details": "SpaceX's first east coast landing pad is Landing Zone 1, where the historic first Falcon 9 landing occurred in December 2015. LC-13 was originally used as a launch pad for early Atlas missiles and rockets from Lockheed Martin. LC-1 was later expanded to include Landing Zone 2 for side booster RTLS Falcon Heavy missions, and it was first used in February 2018 for that purpose.", 31 | "launches": [ 32 | "5eb87d13ffd86e000604b360", 33 | "5eb87d2dffd86e000604b376", 34 | "5eb87d35ffd86e000604b37a" 35 | ], 36 | "id": "5e9e3032383ecb90a834e7c8" 37 | } 38 | ``` 39 | 40 | ## Error Responses 41 | 42 | **Code** : `404 NOT FOUND` 43 | 44 | **Content** : `Not Found` 45 | -------------------------------------------------------------------------------- /docs/v4/landpads/query.md: -------------------------------------------------------------------------------- 1 | # Query landing pads 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/landpads/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "name": "LZ-1", 31 | "full_name": "Landing Zone 1", 32 | "status": "active", 33 | "type": "RTLS", 34 | "locality": "Cape Canaveral", 35 | "region": "Florida", 36 | "latitude": 28.485833, 37 | "longitude": -80.544444, 38 | "landing_attempts": 15, 39 | "landing_successes": 14, 40 | "wikipedia": "https://en.wikipedia.org/wiki/Landing_Zones_1_and_2", 41 | "details": "SpaceX's first east coast landing pad is Landing Zone 1, where the historic first Falcon 9 landing occurred in December 2015. LC-13 was originally used as a launch pad for early Atlas missiles and rockets from Lockheed Martin. LC-1 was later expanded to include Landing Zone 2 for side booster RTLS Falcon Heavy missions, and it was first used in February 2018 for that purpose.", 42 | "launches": [ 43 | "5eb87cefffd86e000604b342", 44 | "5eb87cf9ffd86e000604b349", 45 | "5eb87cfeffd86e000604b34d", 46 | "5eb87d01ffd86e000604b350", 47 | "5eb87d03ffd86e000604b352", 48 | "5eb87d07ffd86e000604b356", 49 | "5eb87d09ffd86e000604b358", 50 | "5eb87d0effd86e000604b35c", 51 | "5eb87d10ffd86e000604b35e", 52 | "5eb87d13ffd86e000604b360", 53 | "5eb87d26ffd86e000604b371", 54 | "5eb87d2dffd86e000604b376", 55 | "5eb87d35ffd86e000604b37a", 56 | "5eb87d36ffd86e000604b37b", 57 | "5eb87d42ffd86e000604b384" 58 | ], 59 | "id": "5e9e3032383ecb267a34e7c7" 60 | }, 61 | ... 62 | ], 63 | "totalDocs": 7, 64 | "offset": 0, 65 | "limit": 10, 66 | "totalPages": 1, 67 | "page": 1, 68 | "pagingCounter": 1, 69 | "hasPrevPage": false, 70 | "hasNextPage": false, 71 | "prevPage": null, 72 | "nextPage": null 73 | } 74 | ``` 75 | 76 | ## Error Responses 77 | 78 | **Code** : `400 Bad Request` 79 | 80 | **Content** : Mongoose error is shown, with suggestions to fix the query. 81 | -------------------------------------------------------------------------------- /docs/v4/landpads/schema.md: -------------------------------------------------------------------------------- 1 | # Landing Pad Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String", 7 | "default": null 8 | }, 9 | "full_name": { 10 | "type": "String", 11 | "default": null 12 | }, 13 | "status": { 14 | "type": "String", 15 | "enum": [ 16 | "active", 17 | "inactive", 18 | "unknown", 19 | "retired", 20 | "lost", 21 | "under construction" 22 | ], 23 | "required": true 24 | }, 25 | "type": { 26 | "type": "String", 27 | "default": null 28 | }, 29 | "locality": { 30 | "type": "String", 31 | "default": null 32 | }, 33 | "region": { 34 | "type": "String", 35 | "default": null 36 | }, 37 | "latitude": { 38 | "type": "Number", 39 | "default": null 40 | }, 41 | "longitude": { 42 | "type": "Number", 43 | "default": null 44 | }, 45 | "landing_attempts": { 46 | "type": "Number", 47 | "default": 0 48 | }, 49 | "landing_successes": { 50 | "type": "Number", 51 | "default": 0 52 | }, 53 | "wikipedia": { 54 | "type": "String", 55 | "default": null 56 | }, 57 | "details": { 58 | "type": "String", 59 | "default": null 60 | }, 61 | "launches": [ 62 | { 63 | "type": "UUID" 64 | } 65 | ] 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/v4/landpads/update.md: -------------------------------------------------------------------------------- 1 | # Update a core 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the capsule 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "status": "expended", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/launches/create.md: -------------------------------------------------------------------------------- 1 | # Create a launch 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launches` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "fairings": null, 14 | "links": { 15 | "patch": { 16 | "small": "https://images2.imgbox.com/53/22/dh0XSLXO_o.png", 17 | "large": "https://images2.imgbox.com/15/2b/NAcsTEB6_o.png" 18 | }, 19 | "reddit": { 20 | "campaign": "https://www.reddit.com/r/spacex/comments/ezn6n0/crs20_launch_campaign_thread", 21 | "launch": "https://www.reddit.com/r/spacex/comments/fe8pcj/rspacex_crs20_official_launch_discussion_updates/", 22 | "media": "https://www.reddit.com/r/spacex/comments/fes64p/rspacex_crs20_media_thread_videos_images_gifs/", 23 | "recovery": null 24 | }, 25 | "flickr": { 26 | "small": [], 27 | "original": [ 28 | "https://live.staticflickr.com/65535/49635401403_96f9c322dc_o.jpg", 29 | "https://live.staticflickr.com/65535/49636202657_e81210a3ca_o.jpg", 30 | "https://live.staticflickr.com/65535/49636202572_8831c5a917_o.jpg", 31 | "https://live.staticflickr.com/65535/49635401423_e0bef3e82f_o.jpg", 32 | "https://live.staticflickr.com/65535/49635985086_660be7062f_o.jpg" 33 | ] 34 | }, 35 | "presskit": "https://www.spacex.com/sites/spacex/files/crs-20_mission_press_kit.pdf", 36 | "webcast": "https://youtu.be/1MkcWK2PnsU", 37 | "youtube_id": "1MkcWK2PnsU", 38 | "article": "https://spaceflightnow.com/2020/03/07/late-night-launch-of-spacex-cargo-ship-marks-end-of-an-era/", 39 | "wikipedia": "https://en.wikipedia.org/wiki/SpaceX_CRS-20" 40 | }, 41 | "static_fire_date_utc": "2020-03-01T10:20:00.000Z", 42 | "static_fire_date_unix": 1583058000, 43 | "tdb": false, 44 | "net": false, 45 | "window": 0, 46 | "rocket": "5e9d0d95eda69973a809d1ec", 47 | "success": true, 48 | "failures": [], 49 | "details": "SpaceX's 20th and final Crew Resupply Mission under the original NASA CRS contract, this mission brings essential supplies to the International Space Station using SpaceX's reusable Dragon spacecraft. It is the last scheduled flight of a Dragon 1 capsule. (CRS-21 and up under the new Commercial Resupply Services 2 contract will use Dragon 2.) The external payload for this mission is the Bartolomeo ISS external payload hosting platform. Falcon 9 and Dragon will launch from SLC-40, Cape Canaveral Air Force Station and the booster will land at LZ-1. The mission will be complete with return and recovery of the Dragon capsule and down cargo.", 50 | "crew": [], 51 | "ships": [], 52 | "capsules": [ 53 | "5e9e2c5cf359185d753b266f" 54 | ], 55 | "payloads": [ 56 | "5eb0e4d0b6c3bb0006eeb253" 57 | ], 58 | "launchpad": "5e9e4501f509094ba4566f84", 59 | "auto_update": true, 60 | "flight_number": 91, 61 | "name": "CRS-20", 62 | "date_utc": "2020-03-07T04:50:31.000Z", 63 | "date_unix": 1583556631, 64 | "date_local": "2020-03-06T23:50:31-05:00", 65 | "date_precision": "hour", 66 | "upcoming": false, 67 | "cores": [ 68 | { 69 | "core": "5e9e28a7f359187afd3b2662", 70 | "flight": 2, 71 | "gridfins": true, 72 | "legs": true, 73 | "reused": true, 74 | "landing_attempt": true, 75 | "landing_success": true, 76 | "landing_type": "RTLS", 77 | "landpad": "5e9e3032383ecb267a34e7c7" 78 | } 79 | ] 80 | } 81 | ``` 82 | 83 | ## Success Response 84 | 85 | **Code** : `201 Created` 86 | 87 | **Content example** : `Created` 88 | 89 | ## Error Responses 90 | 91 | **Code** : `400 Bad Request` 92 | 93 | **Content** : Mongoose error is shown, with suggestions to fix the query. 94 | -------------------------------------------------------------------------------- /docs/v4/launches/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a launch 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launches/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the launch 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/launches/latest.md: -------------------------------------------------------------------------------- 1 | # Get latest launch 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launches/latest` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | { 15 | "fairings": null, 16 | "links": { 17 | "patch": { 18 | "small": "https://images2.imgbox.com/53/22/dh0XSLXO_o.png", 19 | "large": "https://images2.imgbox.com/15/2b/NAcsTEB6_o.png" 20 | }, 21 | "reddit": { 22 | "campaign": "https://www.reddit.com/r/spacex/comments/ezn6n0/crs20_launch_campaign_thread", 23 | "launch": "https://www.reddit.com/r/spacex/comments/fe8pcj/rspacex_crs20_official_launch_discussion_updates/", 24 | "media": "https://www.reddit.com/r/spacex/comments/fes64p/rspacex_crs20_media_thread_videos_images_gifs/", 25 | "recovery": null 26 | }, 27 | "flickr": { 28 | "small": [], 29 | "original": [ 30 | "https://live.staticflickr.com/65535/49635401403_96f9c322dc_o.jpg", 31 | "https://live.staticflickr.com/65535/49636202657_e81210a3ca_o.jpg", 32 | "https://live.staticflickr.com/65535/49636202572_8831c5a917_o.jpg", 33 | "https://live.staticflickr.com/65535/49635401423_e0bef3e82f_o.jpg", 34 | "https://live.staticflickr.com/65535/49635985086_660be7062f_o.jpg" 35 | ] 36 | }, 37 | "presskit": "https://www.spacex.com/sites/spacex/files/crs-20_mission_press_kit.pdf", 38 | "webcast": "https://youtu.be/1MkcWK2PnsU", 39 | "youtube_id": "1MkcWK2PnsU", 40 | "article": "https://spaceflightnow.com/2020/03/07/late-night-launch-of-spacex-cargo-ship-marks-end-of-an-era/", 41 | "wikipedia": "https://en.wikipedia.org/wiki/SpaceX_CRS-20" 42 | }, 43 | "static_fire_date_utc": "2020-03-01T10:20:00.000Z", 44 | "static_fire_date_unix": 1583058000, 45 | "tdb": false, 46 | "net": false, 47 | "window": 0, 48 | "rocket": "5e9d0d95eda69973a809d1ec", 49 | "success": true, 50 | "failures": [], 51 | "details": "SpaceX's 20th and final Crew Resupply Mission under the original NASA CRS contract, this mission brings essential supplies to the International Space Station using SpaceX's reusable Dragon spacecraft. It is the last scheduled flight of a Dragon 1 capsule. (CRS-21 and up under the new Commercial Resupply Services 2 contract will use Dragon 2.) The external payload for this mission is the Bartolomeo ISS external payload hosting platform. Falcon 9 and Dragon will launch from SLC-40, Cape Canaveral Air Force Station and the booster will land at LZ-1. The mission will be complete with return and recovery of the Dragon capsule and down cargo.", 52 | "crew": [], 53 | "ships": [], 54 | "capsules": [ 55 | "5e9e2c5cf359185d753b266f" 56 | ], 57 | "payloads": [ 58 | "5eb0e4d0b6c3bb0006eeb253" 59 | ], 60 | "launchpad": "5e9e4501f509094ba4566f84", 61 | "auto_update": true, 62 | "flight_number": 91, 63 | "name": "CRS-20", 64 | "date_utc": "2020-03-07T04:50:31.000Z", 65 | "date_unix": 1583556631, 66 | "date_local": "2020-03-06T23:50:31-05:00", 67 | "date_precision": "hour", 68 | "upcoming": false, 69 | "cores": [ 70 | { 71 | "core": "5e9e28a7f359187afd3b2662", 72 | "flight": 2, 73 | "gridfins": true, 74 | "legs": true, 75 | "reused": true, 76 | "landing_attempt": true, 77 | "landing_success": true, 78 | "landing_type": "RTLS", 79 | "landpad": "5e9e3032383ecb267a34e7c7" 80 | } 81 | ], 82 | "id": "5eb87d42ffd86e000604b384" 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/v4/launches/next.md: -------------------------------------------------------------------------------- 1 | # Get next launch 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launches/next` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | { 15 | "fairings": null, 16 | "links": { 17 | "patch": { 18 | "small": "https://images2.imgbox.com/53/22/dh0XSLXO_o.png", 19 | "large": "https://images2.imgbox.com/15/2b/NAcsTEB6_o.png" 20 | }, 21 | "reddit": { 22 | "campaign": "https://www.reddit.com/r/spacex/comments/ezn6n0/crs20_launch_campaign_thread", 23 | "launch": "https://www.reddit.com/r/spacex/comments/fe8pcj/rspacex_crs20_official_launch_discussion_updates/", 24 | "media": "https://www.reddit.com/r/spacex/comments/fes64p/rspacex_crs20_media_thread_videos_images_gifs/", 25 | "recovery": null 26 | }, 27 | "flickr": { 28 | "small": [], 29 | "original": [ 30 | "https://live.staticflickr.com/65535/49635401403_96f9c322dc_o.jpg", 31 | "https://live.staticflickr.com/65535/49636202657_e81210a3ca_o.jpg", 32 | "https://live.staticflickr.com/65535/49636202572_8831c5a917_o.jpg", 33 | "https://live.staticflickr.com/65535/49635401423_e0bef3e82f_o.jpg", 34 | "https://live.staticflickr.com/65535/49635985086_660be7062f_o.jpg" 35 | ] 36 | }, 37 | "presskit": "https://www.spacex.com/sites/spacex/files/crs-20_mission_press_kit.pdf", 38 | "webcast": "https://youtu.be/1MkcWK2PnsU", 39 | "youtube_id": "1MkcWK2PnsU", 40 | "article": "https://spaceflightnow.com/2020/03/07/late-night-launch-of-spacex-cargo-ship-marks-end-of-an-era/", 41 | "wikipedia": "https://en.wikipedia.org/wiki/SpaceX_CRS-20" 42 | }, 43 | "static_fire_date_utc": "2020-03-01T10:20:00.000Z", 44 | "static_fire_date_unix": 1583058000, 45 | "tdb": false, 46 | "net": false, 47 | "window": 0, 48 | "rocket": "5e9d0d95eda69973a809d1ec", 49 | "success": true, 50 | "failures": [], 51 | "details": "SpaceX's 20th and final Crew Resupply Mission under the original NASA CRS contract, this mission brings essential supplies to the International Space Station using SpaceX's reusable Dragon spacecraft. It is the last scheduled flight of a Dragon 1 capsule. (CRS-21 and up under the new Commercial Resupply Services 2 contract will use Dragon 2.) The external payload for this mission is the Bartolomeo ISS external payload hosting platform. Falcon 9 and Dragon will launch from SLC-40, Cape Canaveral Air Force Station and the booster will land at LZ-1. The mission will be complete with return and recovery of the Dragon capsule and down cargo.", 52 | "crew": [], 53 | "ships": [], 54 | "capsules": [ 55 | "5e9e2c5cf359185d753b266f" 56 | ], 57 | "payloads": [ 58 | "5eb0e4d0b6c3bb0006eeb253" 59 | ], 60 | "launchpad": "5e9e4501f509094ba4566f84", 61 | "auto_update": true, 62 | "flight_number": 91, 63 | "name": "CRS-20", 64 | "date_utc": "2020-03-07T04:50:31.000Z", 65 | "date_unix": 1583556631, 66 | "date_local": "2020-03-06T23:50:31-05:00", 67 | "date_precision": "hour", 68 | "upcoming": false, 69 | "cores": [ 70 | { 71 | "core": "5e9e28a7f359187afd3b2662", 72 | "flight": 2, 73 | "gridfins": true, 74 | "legs": true, 75 | "reused": true, 76 | "landing_attempt": true, 77 | "landing_success": true, 78 | "landing_type": "RTLS", 79 | "landpad": "5e9e3032383ecb267a34e7c7" 80 | } 81 | ], 82 | "id": "5eb87d42ffd86e000604b384" 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/v4/launches/update.md: -------------------------------------------------------------------------------- 1 | # Update a launch 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launches/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the launch 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "success": true, 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/launchpads/all.md: -------------------------------------------------------------------------------- 1 | # Get all launchpads 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launchpads` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "name": "VAFB SLC 4E", 17 | "full_name": "Vandenberg Air Force Base Space Launch Complex 4E", 18 | "locality": "Vandenberg Air Force Base", 19 | "region": "California", 20 | "timezone": "America/Los_Angeles", 21 | "latitude": 34.632093, 22 | "longitude": -120.610829, 23 | "launch_attempts": 15, 24 | "launch_successes": 15, 25 | "rockets": [ 26 | "5e9d0d95eda69973a809d1ec" 27 | ], 28 | "launches": [ 29 | "5eb87ce1ffd86e000604b334", 30 | "5eb87cf0ffd86e000604b343", 31 | "5eb87cfdffd86e000604b34c", 32 | "5eb87d05ffd86e000604b354", 33 | "5eb87d08ffd86e000604b357", 34 | "5eb87d0affd86e000604b359", 35 | "5eb87d0fffd86e000604b35d", 36 | "5eb87d14ffd86e000604b361", 37 | "5eb87d16ffd86e000604b363", 38 | "5eb87d1affd86e000604b367", 39 | "5eb87d1fffd86e000604b36b", 40 | "5eb87d23ffd86e000604b36e", 41 | "5eb87d25ffd86e000604b370", 42 | "5eb87d28ffd86e000604b373", 43 | "5eb87d31ffd86e000604b379" 44 | ], 45 | "status": "active", 46 | "id": "5e9e4502f509092b78566f87" 47 | }, 48 | ... 49 | ] 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/v4/launchpads/create.md: -------------------------------------------------------------------------------- 1 | # Create a launchpad 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launchpads` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "name": "VAFB SLC 4E", 14 | "full_name": "Vandenberg Air Force Base Space Launch Complex 4E", 15 | "locality": "Vandenberg Air Force Base", 16 | "region": "California", 17 | "timezone": "America/Los_Angeles", 18 | "latitude": 34.632093, 19 | "longitude": -120.610829, 20 | "launch_attempts": 15, 21 | "launch_successes": 15, 22 | "rockets": [ 23 | "5e9d0d95eda69973a809d1ec" 24 | ], 25 | "launches": [ 26 | "5eb87ce1ffd86e000604b334", 27 | "5eb87cf0ffd86e000604b343", 28 | "5eb87cfdffd86e000604b34c", 29 | "5eb87d05ffd86e000604b354", 30 | "5eb87d08ffd86e000604b357", 31 | "5eb87d0affd86e000604b359", 32 | "5eb87d0fffd86e000604b35d", 33 | "5eb87d14ffd86e000604b361", 34 | "5eb87d16ffd86e000604b363", 35 | "5eb87d1affd86e000604b367", 36 | "5eb87d1fffd86e000604b36b", 37 | "5eb87d23ffd86e000604b36e", 38 | "5eb87d25ffd86e000604b370", 39 | "5eb87d28ffd86e000604b373", 40 | "5eb87d31ffd86e000604b379" 41 | ], 42 | "status": "active", 43 | } 44 | ``` 45 | 46 | ## Success Response 47 | 48 | **Code** : `201 Created` 49 | 50 | **Content example** : `Created` 51 | 52 | ## Error Responses 53 | 54 | **Code** : `400 Bad Request` 55 | 56 | **Content** : Mongoose error is shown, with suggestions to fix the query. 57 | -------------------------------------------------------------------------------- /docs/v4/launchpads/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a launchpad 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launchpads/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the launchpad 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/launchpads/one.md: -------------------------------------------------------------------------------- 1 | # Get one launchpad 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launchpads/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the launchpad 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "name": "VAFB SLC 4E", 20 | "full_name": "Vandenberg Air Force Base Space Launch Complex 4E", 21 | "locality": "Vandenberg Air Force Base", 22 | "region": "California", 23 | "timezone": "America/Los_Angeles", 24 | "latitude": 34.632093, 25 | "longitude": -120.610829, 26 | "launch_attempts": 15, 27 | "launch_successes": 15, 28 | "rockets": [ 29 | "5e9d0d95eda69973a809d1ec" 30 | ], 31 | "launches": [ 32 | "5eb87ce1ffd86e000604b334", 33 | "5eb87cf0ffd86e000604b343", 34 | "5eb87cfdffd86e000604b34c", 35 | "5eb87d05ffd86e000604b354", 36 | "5eb87d08ffd86e000604b357", 37 | "5eb87d0affd86e000604b359", 38 | "5eb87d0fffd86e000604b35d", 39 | "5eb87d14ffd86e000604b361", 40 | "5eb87d16ffd86e000604b363", 41 | "5eb87d1affd86e000604b367", 42 | "5eb87d1fffd86e000604b36b", 43 | "5eb87d23ffd86e000604b36e", 44 | "5eb87d25ffd86e000604b370", 45 | "5eb87d28ffd86e000604b373", 46 | "5eb87d31ffd86e000604b379" 47 | ], 48 | "status": "active", 49 | "id": "5e9e4502f509092b78566f87" 50 | } 51 | ``` 52 | 53 | ## Error Responses 54 | 55 | **Code** : `404 NOT FOUND` 56 | 57 | **Content** : `Not Found` 58 | -------------------------------------------------------------------------------- /docs/v4/launchpads/query.md: -------------------------------------------------------------------------------- 1 | # Query launchpads 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/launchpads/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "name": "VAFB SLC 4E", 31 | "full_name": "Vandenberg Air Force Base Space Launch Complex 4E", 32 | "locality": "Vandenberg Air Force Base", 33 | "region": "California", 34 | "timezone": "America/Los_Angeles", 35 | "latitude": 34.632093, 36 | "longitude": -120.610829, 37 | "launch_attempts": 15, 38 | "launch_successes": 15, 39 | "rockets": [ 40 | "5e9d0d95eda69973a809d1ec" 41 | ], 42 | "launches": [ 43 | "5eb87ce1ffd86e000604b334", 44 | "5eb87cf0ffd86e000604b343", 45 | "5eb87cfdffd86e000604b34c", 46 | "5eb87d05ffd86e000604b354", 47 | "5eb87d08ffd86e000604b357", 48 | "5eb87d0affd86e000604b359", 49 | "5eb87d0fffd86e000604b35d", 50 | "5eb87d14ffd86e000604b361", 51 | "5eb87d16ffd86e000604b363", 52 | "5eb87d1affd86e000604b367", 53 | "5eb87d1fffd86e000604b36b", 54 | "5eb87d23ffd86e000604b36e", 55 | "5eb87d25ffd86e000604b370", 56 | "5eb87d28ffd86e000604b373", 57 | "5eb87d31ffd86e000604b379" 58 | ], 59 | "status": "active", 60 | "id": "5e9e4502f509092b78566f87" 61 | }, 62 | ... 63 | ], 64 | "totalDocs": 6, 65 | "offset": 0, 66 | "limit": 10, 67 | "totalPages": 1, 68 | "page": 1, 69 | "pagingCounter": 1, 70 | "hasPrevPage": false, 71 | "hasNextPage": false, 72 | "prevPage": null, 73 | "nextPage": null 74 | } 75 | ``` 76 | 77 | ## Error Responses 78 | 79 | **Code** : `400 Bad Request` 80 | 81 | **Content** : Mongoose error is shown, with suggestions to fix the query. 82 | -------------------------------------------------------------------------------- /docs/v4/launchpads/schema.md: -------------------------------------------------------------------------------- 1 | # Launchpad Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String", 7 | "default": null 8 | }, 9 | "full_name": { 10 | "type": "String", 11 | "default": null 12 | }, 13 | "status": { 14 | "type": "String", 15 | "enum": [ 16 | "active", 17 | "inactive", 18 | "unknown", 19 | "retired", 20 | "lost", 21 | "under construction" 22 | ], 23 | "required": true 24 | }, 25 | "locality": { 26 | "type": "String", 27 | "default": null 28 | }, 29 | "region": { 30 | "type": "String", 31 | "default": null 32 | }, 33 | "timezone": { 34 | "type": "String", 35 | "default": null 36 | }, 37 | "latitude": { 38 | "type": "Number", 39 | "default": null 40 | }, 41 | "longitude": { 42 | "type": "Number", 43 | "default": null 44 | }, 45 | "launch_attempts": { 46 | "type": "Number", 47 | "default": 0 48 | }, 49 | "launch_successes": { 50 | "type": "Number", 51 | "default": 0 52 | }, 53 | "rockets": [ 54 | "UUID" 55 | ], 56 | "launches": [ 57 | "UUID" 58 | ] 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/v4/launchpads/update.md: -------------------------------------------------------------------------------- 1 | # Update a core 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/cores/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the capsule 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "status": "expended", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/payloads/all.md: -------------------------------------------------------------------------------- 1 | # Get all payloads 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/payloads` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "dragon": { 17 | "capsule": null, 18 | "mass_returned_kg": null, 19 | "mass_returned_lbs": null, 20 | "flight_time_sec": null, 21 | "manifest": null, 22 | "water_landing": null, 23 | "land_landing": null 24 | }, 25 | "name": "Tintin A & B", 26 | "type": "Satellite", 27 | "reused": false, 28 | "launch": "5eb87d14ffd86e000604b361", 29 | "customers": [ 30 | "SpaceX" 31 | ], 32 | "norad_ids": [ 33 | 43216, 34 | 43217 35 | ], 36 | "nationalities": [ 37 | "United States" 38 | ], 39 | "manufacturers": [ 40 | "SpaceX" 41 | ], 42 | "mass_kg": 800, 43 | "mass_lbs": 1763.7, 44 | "orbit": "SSO", 45 | "reference_system": "geocentric", 46 | "regime": "low-earth", 47 | "longitude": null, 48 | "semi_major_axis_km": 6737.42, 49 | "eccentricity": 0.0012995, 50 | "periapsis_km": 350.53, 51 | "apoapsis_km": 368.04, 52 | "inclination_deg": 97.4444, 53 | "period_min": 91.727, 54 | "lifespan_years": 1, 55 | "epoch": "2020-06-13T13:46:31.000Z", 56 | "mean_motion": 15.69864906, 57 | "raan": 176.6734, 58 | "arg_of_pericenter": 174.2326, 59 | "mean_anomaly": 185.9087, 60 | "id": "5eb0e4c6b6c3bb0006eeb21e" 61 | }, 62 | ... 63 | ] 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/v4/payloads/create.md: -------------------------------------------------------------------------------- 1 | # Create a payload 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/payloads` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "dragon": { 14 | "capsule": null, 15 | "mass_returned_kg": null, 16 | "mass_returned_lbs": null, 17 | "flight_time_sec": null, 18 | "manifest": null, 19 | "water_landing": null, 20 | "land_landing": null 21 | }, 22 | "name": "Tintin A & B", 23 | "type": "Satellite", 24 | "reused": false, 25 | "launch": "5eb87d14ffd86e000604b361", 26 | "customers": [ 27 | "SpaceX" 28 | ], 29 | "norad_ids": [ 30 | 43216, 31 | 43217 32 | ], 33 | "nationalities": [ 34 | "United States" 35 | ], 36 | "manufacturers": [ 37 | "SpaceX" 38 | ], 39 | "mass_kg": 800, 40 | "mass_lbs": 1763.7, 41 | "orbit": "SSO", 42 | "reference_system": "geocentric", 43 | "regime": "low-earth", 44 | "longitude": null, 45 | "semi_major_axis_km": 6737.42, 46 | "eccentricity": 0.0012995, 47 | "periapsis_km": 350.53, 48 | "apoapsis_km": 368.04, 49 | "inclination_deg": 97.4444, 50 | "period_min": 91.727, 51 | "lifespan_years": 1, 52 | "epoch": "2020-06-13T13:46:31.000Z", 53 | "mean_motion": 15.69864906, 54 | "raan": 176.6734, 55 | "arg_of_pericenter": 174.2326, 56 | "mean_anomaly": 185.9087, 57 | } 58 | ``` 59 | 60 | ## Success Response 61 | 62 | **Code** : `201 Created` 63 | 64 | **Content example** : `Created` 65 | 66 | ## Error Responses 67 | 68 | **Code** : `400 Bad Request` 69 | 70 | **Content** : Mongoose error is shown, with suggestions to fix the query. 71 | -------------------------------------------------------------------------------- /docs/v4/payloads/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a payload 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/payloads/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the payload 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/payloads/one.md: -------------------------------------------------------------------------------- 1 | # Get one payload 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/payloads/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the payload 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "dragon": { 20 | "capsule": null, 21 | "mass_returned_kg": null, 22 | "mass_returned_lbs": null, 23 | "flight_time_sec": null, 24 | "manifest": null, 25 | "water_landing": null, 26 | "land_landing": null 27 | }, 28 | "name": "Tintin A & B", 29 | "type": "Satellite", 30 | "reused": false, 31 | "launch": "5eb87d14ffd86e000604b361", 32 | "customers": [ 33 | "SpaceX" 34 | ], 35 | "norad_ids": [ 36 | 43216, 37 | 43217 38 | ], 39 | "nationalities": [ 40 | "United States" 41 | ], 42 | "manufacturers": [ 43 | "SpaceX" 44 | ], 45 | "mass_kg": 800, 46 | "mass_lbs": 1763.7, 47 | "orbit": "SSO", 48 | "reference_system": "geocentric", 49 | "regime": "low-earth", 50 | "longitude": null, 51 | "semi_major_axis_km": 6737.42, 52 | "eccentricity": 0.0012995, 53 | "periapsis_km": 350.53, 54 | "apoapsis_km": 368.04, 55 | "inclination_deg": 97.4444, 56 | "period_min": 91.727, 57 | "lifespan_years": 1, 58 | "epoch": "2020-06-13T13:46:31.000Z", 59 | "mean_motion": 15.69864906, 60 | "raan": 176.6734, 61 | "arg_of_pericenter": 174.2326, 62 | "mean_anomaly": 185.9087, 63 | "id": "5eb0e4c6b6c3bb0006eeb21e" 64 | } 65 | ``` 66 | 67 | ## Error Responses 68 | 69 | **Code** : `404 NOT FOUND` 70 | 71 | **Content** : `Not Found` 72 | -------------------------------------------------------------------------------- /docs/v4/payloads/query.md: -------------------------------------------------------------------------------- 1 | # Query payloads 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/payloads/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "dragon": { 31 | "capsule": null, 32 | "mass_returned_kg": null, 33 | "mass_returned_lbs": null, 34 | "flight_time_sec": null, 35 | "manifest": null, 36 | "water_landing": null, 37 | "land_landing": null 38 | }, 39 | "name": "Tintin A & B", 40 | "type": "Satellite", 41 | "reused": false, 42 | "launch": "5eb87d14ffd86e000604b361", 43 | "customers": [ 44 | "SpaceX" 45 | ], 46 | "norad_ids": [ 47 | 43216, 48 | 43217 49 | ], 50 | "nationalities": [ 51 | "United States" 52 | ], 53 | "manufacturers": [ 54 | "SpaceX" 55 | ], 56 | "mass_kg": 800, 57 | "mass_lbs": 1763.7, 58 | "orbit": "SSO", 59 | "reference_system": "geocentric", 60 | "regime": "low-earth", 61 | "longitude": null, 62 | "semi_major_axis_km": 6737.42, 63 | "eccentricity": 0.0012995, 64 | "periapsis_km": 350.53, 65 | "apoapsis_km": 368.04, 66 | "inclination_deg": 97.4444, 67 | "period_min": 91.727, 68 | "lifespan_years": 1, 69 | "epoch": "2020-06-13T13:46:31.000Z", 70 | "mean_motion": 15.69864906, 71 | "raan": 176.6734, 72 | "arg_of_pericenter": 174.2326, 73 | "mean_anomaly": 185.9087, 74 | "id": "5eb0e4c6b6c3bb0006eeb21e" 75 | } 76 | ... 77 | ], 78 | "totalDocs": 136, 79 | "offset": 0, 80 | "limit": 10, 81 | "totalPages": 14, 82 | "page": 1, 83 | "pagingCounter": 1, 84 | "hasPrevPage": false, 85 | "hasNextPage": true, 86 | "prevPage": null, 87 | "nextPage": 2 88 | } 89 | ``` 90 | 91 | ## Error Responses 92 | 93 | **Code** : `400 Bad Request` 94 | 95 | **Content** : Mongoose error is shown, with suggestions to fix the query. 96 | -------------------------------------------------------------------------------- /docs/v4/payloads/schema.md: -------------------------------------------------------------------------------- 1 | # Payload Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String", 7 | "default": null, 8 | "unique": true 9 | }, 10 | "type": { 11 | "type": "String", 12 | "default": null 13 | }, 14 | "reused": { 15 | "type": "Boolean", 16 | "default": false 17 | }, 18 | "launch": { 19 | "type": "UUID", 20 | "default": null 21 | }, 22 | "customers": [ 23 | "String" 24 | ], 25 | "norad_ids": [ 26 | "Number" 27 | ], 28 | "nationalities": [ 29 | "String" 30 | ], 31 | "manufacturers": [ 32 | "String" 33 | ], 34 | "mass_kg": { 35 | "type": "Number", 36 | "default": null 37 | }, 38 | "mass_lbs": { 39 | "type": "Number", 40 | "default": null 41 | }, 42 | "orbit": { 43 | "type": "String", 44 | "default": null 45 | }, 46 | "reference_system": { 47 | "type": "String", 48 | "default": null 49 | }, 50 | "regime": { 51 | "type": "String", 52 | "default": null 53 | }, 54 | "longitude": { 55 | "type": "Number", 56 | "default": null 57 | }, 58 | "semi_major_axis_km": { 59 | "type": "Number", 60 | "default": null 61 | }, 62 | "eccentricity": { 63 | "type": "Number", 64 | "default": null 65 | }, 66 | "periapsis_km": { 67 | "type": "Number", 68 | "default": null 69 | }, 70 | "apoapsis_km": { 71 | "type": "Number", 72 | "default": null 73 | }, 74 | "inclination_deg": { 75 | "type": "Number", 76 | "default": null 77 | }, 78 | "period_min": { 79 | "type": "Number", 80 | "default": null 81 | }, 82 | "lifespan_years": { 83 | "type": "Number", 84 | "default": null 85 | }, 86 | "epoch": { 87 | "type": "String", 88 | "default": null 89 | }, 90 | "mean_motion": { 91 | "type": "Number", 92 | "default": null 93 | }, 94 | "raan": { 95 | "type": "Number", 96 | "default": null 97 | }, 98 | "arg_of_pericenter": { 99 | "type": "Number", 100 | "default": null 101 | }, 102 | "mean_anomaly": { 103 | "type": "Number", 104 | "default": null 105 | }, 106 | "dragon": { 107 | "capsule": { 108 | "type": "UUID", 109 | "default": null 110 | }, 111 | "mass_returned_kg": { 112 | "type": "Number", 113 | "default": null 114 | }, 115 | "mass_returned_lbs": { 116 | "type": "Number", 117 | "default": null 118 | }, 119 | "flight_time_sec": { 120 | "type": "Number", 121 | "default": null 122 | }, 123 | "manifest": { 124 | "type": "String", 125 | "default": null 126 | }, 127 | "water_landing": { 128 | "type": "Boolean", 129 | "default": null 130 | }, 131 | "land_landing": { 132 | "type": "Boolean", 133 | "default": null 134 | } 135 | } 136 | } 137 | ``` 138 | -------------------------------------------------------------------------------- /docs/v4/payloads/update.md: -------------------------------------------------------------------------------- 1 | # Update a payload 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/payloads/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the payload 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "reused": true, 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/roadster/get.md: -------------------------------------------------------------------------------- 1 | # Get roadster info 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/roadster` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | { 15 | "flickr_images": [ 16 | "https://farm5.staticflickr.com/4615/40143096241_11128929df_b.jpg", 17 | "https://farm5.staticflickr.com/4702/40110298232_91b32d0cc0_b.jpg", 18 | "https://farm5.staticflickr.com/4676/40110297852_5e794b3258_b.jpg", 19 | "https://farm5.staticflickr.com/4745/40110304192_6e3e9a7a1b_b.jpg" 20 | ], 21 | "name": "Elon Musk's Tesla Roadster", 22 | "launch_date_utc": "2018-02-06T20:45:00.000Z", 23 | "launch_date_unix": 1517949900, 24 | "launch_mass_kg": 1350, 25 | "launch_mass_lbs": 2976, 26 | "norad_id": 43205, 27 | "epoch_jd": 2459014.345891204, 28 | "orbit_type": "heliocentric", 29 | "apoapsis_au": 1.663950009802517, 30 | "periapsis_au": 0.9859657216725529, 31 | "semi_major_axis_au": 196.2991348009594, 32 | "eccentricity": 0.2558512635239784, 33 | "inclination": 1.077499248052439, 34 | "longitude": 317.0839961949045, 35 | "periapsis_arg": 177.5240278992875, 36 | "period_days": 557.059427465354, 37 | "speed_kph": 72209.97792, 38 | "speed_mph": 44869.18619012833, 39 | "earth_distance_km": 220606726.83228922, 40 | "earth_distance_mi": 137078622.45850638, 41 | "mars_distance_km": 89348334.47067611, 42 | "mars_distance_mi": 55518463.93837848, 43 | "wikipedia": "https://en.wikipedia.org/wiki/Elon_Musk%27s_Tesla_Roadster", 44 | "video": "https://youtu.be/wbSwFU6tY1c", 45 | "details": "Elon Musk's Tesla Roadster is an electric sports car that served as the dummy payload for the February 2018 Falcon Heavy test flight and is now an artificial satellite of the Sun. Starman, a mannequin dressed in a spacesuit, occupies the driver's seat. The car and rocket are products of Tesla and SpaceX. This 2008-model Roadster was previously used by Musk for commuting, and is the only consumer car sent into space.", 46 | "id": "5eb75f0842fea42237d7f3f4" 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/v4/roadster/schema.md: -------------------------------------------------------------------------------- 1 | # Roadster Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String" 7 | }, 8 | "launch_date_utc": { 9 | "type": "String" 10 | }, 11 | "launch_date_unix": { 12 | "type": "Number" 13 | }, 14 | "launch_mass_kg": { 15 | "type": "Number" 16 | }, 17 | "launch_mass_lbs": { 18 | "type": "Number" 19 | }, 20 | "norad_id": { 21 | "type": "Number" 22 | }, 23 | "epoch_jd": { 24 | "type": "Number" 25 | }, 26 | "orbit_type": { 27 | "type": "String" 28 | }, 29 | "apoapsis_au": { 30 | "type": "Number" 31 | }, 32 | "periapsis_au": { 33 | "type": "Number" 34 | }, 35 | "semi_major_axis_au": { 36 | "type": "Number" 37 | }, 38 | "eccentricity": { 39 | "type": "Number" 40 | }, 41 | "inclination": { 42 | "type": "Number" 43 | }, 44 | "longitude": { 45 | "type": "Number" 46 | }, 47 | "periapsis_arg": { 48 | "type": "Number" 49 | }, 50 | "period_days": { 51 | "type": "Number" 52 | }, 53 | "speed_kph": { 54 | "type": "Number" 55 | }, 56 | "speed_mph": { 57 | "type": "Number" 58 | }, 59 | "earth_distance_km": { 60 | "type": "Number" 61 | }, 62 | "earth_distance_mi": { 63 | "type": "Number" 64 | }, 65 | "mars_distance_km": { 66 | "type": "Number" 67 | }, 68 | "mars_distance_mi": { 69 | "type": "Number" 70 | }, 71 | "flickr_images": [ 72 | "String" 73 | ], 74 | "wikipedia": { 75 | "type": "String" 76 | }, 77 | "video": { 78 | "type": "String" 79 | }, 80 | "details": { 81 | "type": "String" 82 | } 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/v4/roadster/update.md: -------------------------------------------------------------------------------- 1 | # Update roadster info 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/roadster/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the roadster 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "video": "https://youtu.be/wbSwFU6tY1c" 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/rockets/all.md: -------------------------------------------------------------------------------- 1 | # Get all rockets 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/rockets` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "height": { 17 | "meters": 70, 18 | "feet": 229.6 19 | }, 20 | "diameter": { 21 | "meters": 12.2, 22 | "feet": 39.9 23 | }, 24 | "mass": { 25 | "kg": 1420788, 26 | "lb": 3125735 27 | }, 28 | "first_stage": { 29 | "thrust_sea_level": { 30 | "kN": 22819, 31 | "lbf": 5130000 32 | }, 33 | "thrust_vacuum": { 34 | "kN": 24681, 35 | "lbf": 5548500 36 | }, 37 | "reusable": true, 38 | "engines": 27, 39 | "fuel_amount_tons": 1155, 40 | "burn_time_sec": 162 41 | }, 42 | "second_stage": { 43 | "thrust": { 44 | "kN": 934, 45 | "lbf": 210000 46 | }, 47 | "payloads": { 48 | "composite_fairing": { 49 | "height": { 50 | "meters": 13.1, 51 | "feet": 43 52 | }, 53 | "diameter": { 54 | "meters": 5.2, 55 | "feet": 17.1 56 | } 57 | }, 58 | "option_1": "dragon" 59 | }, 60 | "reusable": false, 61 | "engines": 1, 62 | "fuel_amount_tons": 90, 63 | "burn_time_sec": 397 64 | }, 65 | "engines": { 66 | "isp": { 67 | "sea_level": 288, 68 | "vacuum": 312 69 | }, 70 | "thrust_sea_level": { 71 | "kN": 845, 72 | "lbf": 190000 73 | }, 74 | "thrust_vacuum": { 75 | "kN": 914, 76 | "lbf": 205500 77 | }, 78 | "number": 27, 79 | "type": "merlin", 80 | "version": "1D+", 81 | "layout": "octaweb", 82 | "engine_loss_max": 6, 83 | "propellant_1": "liquid oxygen", 84 | "propellant_2": "RP-1 kerosene", 85 | "thrust_to_weight": 180.1 86 | }, 87 | "landing_legs": { 88 | "number": 12, 89 | "material": "carbon fiber" 90 | }, 91 | "payload_weights": [ 92 | { 93 | "id": "leo", 94 | "name": "Low Earth Orbit", 95 | "kg": 63800, 96 | "lb": 140660 97 | }, 98 | { 99 | "id": "gto", 100 | "name": "Geosynchronous Transfer Orbit", 101 | "kg": 26700, 102 | "lb": 58860 103 | }, 104 | { 105 | "id": "mars", 106 | "name": "Mars Orbit", 107 | "kg": 16800, 108 | "lb": 37040 109 | }, 110 | { 111 | "id": "pluto", 112 | "name": "Pluto Orbit", 113 | "kg": 3500, 114 | "lb": 7720 115 | } 116 | ], 117 | "flickr_images": [ 118 | "https://farm5.staticflickr.com/4599/38583829295_581f34dd84_b.jpg", 119 | "https://farm5.staticflickr.com/4645/38583830575_3f0f7215e6_b.jpg", 120 | "https://farm5.staticflickr.com/4696/40126460511_b15bf84c85_b.jpg", 121 | "https://farm5.staticflickr.com/4711/40126461411_aabc643fd8_b.jpg" 122 | ], 123 | "name": "Falcon Heavy", 124 | "type": "rocket", 125 | "active": true, 126 | "stages": 2, 127 | "boosters": 2, 128 | "cost_per_launch": 90000000, 129 | "success_rate_pct": 100, 130 | "first_flight": "2018-02-06", 131 | "country": "United States", 132 | "company": "SpaceX", 133 | "wikipedia": "https://en.wikipedia.org/wiki/Falcon_Heavy", 134 | "description": "With the ability to lift into orbit over 54 metric tons (119,000 lb)--a mass equivalent to a 737 jetliner loaded with passengers, crew, luggage and fuel--Falcon Heavy can lift more than twice the payload of the next closest operational vehicle, the Delta IV Heavy, at one-third the cost.", 135 | "id": "5e9d0d95eda69974db09d1ed" 136 | }, 137 | ... 138 | ] 139 | ``` 140 | -------------------------------------------------------------------------------- /docs/v4/rockets/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a rocket 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/rockets/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the rocket 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/rockets/update.md: -------------------------------------------------------------------------------- 1 | # Update a rocket 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/rockets/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the rocket 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "flickr_images": [ 16 | "https://farm5.staticflickr.com/4599/38583829295_581f34dd84_b.jpg" 17 | ] 18 | } 19 | ``` 20 | 21 | ## Success Response 22 | 23 | **Code** : `200 OK` 24 | 25 | **Content example** : `OK` 26 | 27 | ## Error Responses 28 | 29 | **Code** : `400 Bad Request` 30 | 31 | **Content** : Mongoose error is shown, with suggestions to fix the query. 32 | -------------------------------------------------------------------------------- /docs/v4/ships/all.md: -------------------------------------------------------------------------------- 1 | # Get all ships 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/ships` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "legacy_id": "GOPURSUIT", 17 | "model": null, 18 | "type": "Cargo", 19 | "roles": [ 20 | "Support Ship", 21 | "Fairing Recovery" 22 | ], 23 | "imo": 9458884, 24 | "mmsi": 367191410, 25 | "abs": 1201189, 26 | "class": 7174230, 27 | "mass_kg": 502999, 28 | "mass_lbs": 1108925, 29 | "year_built": 2007, 30 | "home_port": "Port Canaveral", 31 | "status": "", 32 | "speed_kn": null, 33 | "course_deg": null, 34 | "latitude": null, 35 | "longitude": null, 36 | "last_ais_update": null, 37 | "link": "https://www.marinetraffic.com/en/ais/details/ships/shipid:439594/mmsi:367191410/imo:9458884/vessel:GO_PURSUIT", 38 | "image": "https://i.imgur.com/5w1ZWre.jpg", 39 | "launches": [ 40 | "5eb87d18ffd86e000604b365", 41 | "5eb87d19ffd86e000604b366", 42 | "5eb87d1bffd86e000604b368", 43 | "5eb87d1effd86e000604b36a" 44 | ], 45 | "name": "GO Pursuit", 46 | "active": false, 47 | "id": "5ea6ed2e080df4000697c90a" 48 | }, 49 | ... 50 | ] 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/v4/ships/create.md: -------------------------------------------------------------------------------- 1 | # Create a ship 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/ships` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "legacy_id": "GOPURSUIT", 14 | "model": null, 15 | "type": "Cargo", 16 | "roles": [ 17 | "Support Ship", 18 | "Fairing Recovery" 19 | ], 20 | "imo": 9458884, 21 | "mmsi": 367191410, 22 | "abs": 1201189, 23 | "class": 7174230, 24 | "mass_kg": 502999, 25 | "mass_lbs": 1108925, 26 | "year_built": 2007, 27 | "home_port": "Port Canaveral", 28 | "status": "", 29 | "speed_kn": null, 30 | "course_deg": null, 31 | "latitude": null, 32 | "longitude": null, 33 | "last_ais_update": null, 34 | "link": "https://www.marinetraffic.com/en/ais/details/ships/shipid:439594/mmsi:367191410/imo:9458884/vessel:GO_PURSUIT", 35 | "image": "https://i.imgur.com/5w1ZWre.jpg", 36 | "launches": [ 37 | "5eb87d18ffd86e000604b365", 38 | "5eb87d19ffd86e000604b366", 39 | "5eb87d1bffd86e000604b368", 40 | "5eb87d1effd86e000604b36a" 41 | ], 42 | "name": "GO Pursuit", 43 | "active": false 44 | } 45 | ``` 46 | 47 | ## Success Response 48 | 49 | **Code** : `201 Created` 50 | 51 | **Content example** : `Created` 52 | 53 | ## Error Responses 54 | 55 | **Code** : `400 Bad Request` 56 | 57 | **Content** : Mongoose error is shown, with suggestions to fix the query. 58 | -------------------------------------------------------------------------------- /docs/v4/ships/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a ship 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/ships/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the ship 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/ships/one.md: -------------------------------------------------------------------------------- 1 | # Get one ship 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/ships/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the ship 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "legacy_id": "GOPURSUIT", 20 | "model": null, 21 | "type": "Cargo", 22 | "roles": [ 23 | "Support Ship", 24 | "Fairing Recovery" 25 | ], 26 | "imo": 9458884, 27 | "mmsi": 367191410, 28 | "abs": 1201189, 29 | "class": 7174230, 30 | "mass_kg": 502999, 31 | "mass_lbs": 1108925, 32 | "year_built": 2007, 33 | "home_port": "Port Canaveral", 34 | "status": "", 35 | "speed_kn": null, 36 | "course_deg": null, 37 | "latitude": null, 38 | "longitude": null, 39 | "last_ais_update": null, 40 | "link": "https://www.marinetraffic.com/en/ais/details/ships/shipid:439594/mmsi:367191410/imo:9458884/vessel:GO_PURSUIT", 41 | "image": "https://i.imgur.com/5w1ZWre.jpg", 42 | "launches": [ 43 | "5eb87d18ffd86e000604b365", 44 | "5eb87d19ffd86e000604b366", 45 | "5eb87d1bffd86e000604b368", 46 | "5eb87d1effd86e000604b36a" 47 | ], 48 | "name": "GO Pursuit", 49 | "active": false, 50 | "id": "5ea6ed2e080df4000697c90a" 51 | } 52 | ``` 53 | 54 | ## Error Responses 55 | 56 | **Code** : `404 NOT FOUND` 57 | 58 | **Content** : `Not Found` 59 | -------------------------------------------------------------------------------- /docs/v4/ships/query.md: -------------------------------------------------------------------------------- 1 | # Query ships 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/ships/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "legacy_id": "GOPURSUIT", 31 | "model": null, 32 | "type": "Cargo", 33 | "roles": [ 34 | "Support Ship", 35 | "Fairing Recovery" 36 | ], 37 | "imo": 9458884, 38 | "mmsi": 367191410, 39 | "abs": 1201189, 40 | "class": 7174230, 41 | "mass_kg": 502999, 42 | "mass_lbs": 1108925, 43 | "year_built": 2007, 44 | "home_port": "Port Canaveral", 45 | "status": "", 46 | "speed_kn": null, 47 | "course_deg": null, 48 | "latitude": null, 49 | "longitude": null, 50 | "last_ais_update": null, 51 | "link": "https://www.marinetraffic.com/en/ais/details/ships/shipid:439594/mmsi:367191410/imo:9458884/vessel:GO_PURSUIT", 52 | "image": "https://i.imgur.com/5w1ZWre.jpg", 53 | "launches": [ 54 | "5eb87d18ffd86e000604b365", 55 | "5eb87d19ffd86e000604b366", 56 | "5eb87d1bffd86e000604b368", 57 | "5eb87d1effd86e000604b36a" 58 | ], 59 | "name": "GO Pursuit", 60 | "active": false, 61 | "id": "5ea6ed2e080df4000697c90a" 62 | }, 63 | ... 64 | ], 65 | "totalDocs": 22, 66 | "offset": 0, 67 | "limit": 10, 68 | "totalPages": 3, 69 | "page": 1, 70 | "pagingCounter": 1, 71 | "hasPrevPage": false, 72 | "hasNextPage": true, 73 | "prevPage": null, 74 | "nextPage": 2 75 | } 76 | ``` 77 | 78 | ## Error Responses 79 | 80 | **Code** : `400 Bad Request` 81 | 82 | **Content** : Mongoose error is shown, with suggestions to fix the query. 83 | -------------------------------------------------------------------------------- /docs/v4/ships/schema.md: -------------------------------------------------------------------------------- 1 | # Ship Schema 2 | 3 | ```json 4 | { 5 | "name": { 6 | "type": "String", 7 | "unique": true, 8 | "required": true 9 | }, 10 | "legacy_id": { 11 | "type": "String", 12 | "default": null 13 | }, 14 | "model": { 15 | "type": "String", 16 | "default": null 17 | }, 18 | "type": { 19 | "type": "String", 20 | "default": null 21 | }, 22 | "roles": [ 23 | "String" 24 | ], 25 | "active": { 26 | "type": "Boolean", 27 | "required": true 28 | }, 29 | "imo": { 30 | "type": "Number", 31 | "default": null 32 | }, 33 | "mmsi": { 34 | "type": "Number", 35 | "default": null 36 | }, 37 | "abs": { 38 | "type": "Number", 39 | "default": null 40 | }, 41 | "class": { 42 | "type": "Number", 43 | "default": null 44 | }, 45 | "mass_kg": { 46 | "type": "Number", 47 | "default": null 48 | }, 49 | "mass_lbs": { 50 | "type": "Number", 51 | "default": null 52 | }, 53 | "year_built": { 54 | "type": "Number", 55 | "default": null 56 | }, 57 | "home_port": { 58 | "type": "String", 59 | "default": null 60 | }, 61 | "status": { 62 | "type": "String", 63 | "default": null 64 | }, 65 | "speed_kn": { 66 | "type": "Number", 67 | "default": null 68 | }, 69 | "course_deg": { 70 | "type": "Number", 71 | "default": null 72 | }, 73 | "latitude": { 74 | "type": "Number", 75 | "default": null 76 | }, 77 | "longitude": { 78 | "type": "Number", 79 | "default": null 80 | }, 81 | "last_ais_update": { 82 | "type": "String", 83 | "default": null 84 | }, 85 | "link": { 86 | "type": "String", 87 | "default": null 88 | }, 89 | "image": { 90 | "type": "String", 91 | "default": null 92 | }, 93 | "launches": [ 94 | { 95 | "type": "UUID" 96 | } 97 | ] 98 | } 99 | ``` 100 | -------------------------------------------------------------------------------- /docs/v4/ships/update.md: -------------------------------------------------------------------------------- 1 | # Update a ship 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/ships/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the ship 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "home_port": "Port Canaveral", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /docs/v4/starlink/all.md: -------------------------------------------------------------------------------- 1 | # Get all Starlink satellites 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/starlink` 6 | 7 | **Auth required** : `False` 8 | 9 | ## Success Responses 10 | 11 | **Code** : `200 OK` 12 | 13 | ```json 14 | [ 15 | { 16 | "spaceTrack": { 17 | "CCSDS_OMM_VERS": "2.0", 18 | "COMMENT": "GENERATED VIA SPACE-TRACK.ORG API", 19 | "CREATION_DATE": "2020-06-19 21:46:09", 20 | "ORIGINATOR": "18 SPCS", 21 | "OBJECT_NAME": "STARLINK-1506", 22 | "OBJECT_ID": "2020-038T", 23 | "CENTER_NAME": "EARTH", 24 | "REF_FRAME": "TEME", 25 | "TIME_SYSTEM": "UTC", 26 | "MEAN_ELEMENT_THEORY": "SGP4", 27 | "EPOCH": "2020-06-19 20:00:01.000224", 28 | "MEAN_MOTION": 15.88829743, 29 | "ECCENTRICITY": 0.0087515, 30 | "INCLINATION": 53.002, 31 | "RA_OF_ASC_NODE": 266.3302, 32 | "ARG_OF_PERICENTER": 69.9474, 33 | "MEAN_ANOMALY": 221.4733, 34 | "EPHEMERIS_TYPE": 0, 35 | "CLASSIFICATION_TYPE": "U", 36 | "NORAD_CAT_ID": 45747, 37 | "ELEMENT_SET_NO": 999, 38 | "REV_AT_EPOCH": 212, 39 | "BSTAR": 0.01007, 40 | "MEAN_MOTION_DOT": 0.03503094, 41 | "MEAN_MOTION_DDOT": 0.01265, 42 | "SEMIMAJOR_AXIS": 6683.699, 43 | "PERIOD": 90.632, 44 | "APOAPSIS": 364.057, 45 | "PERIAPSIS": 247.072, 46 | "OBJECT_TYPE": "PAYLOAD", 47 | "RCS_SIZE": null, 48 | "COUNTRY_CODE": "US", 49 | "LAUNCH_DATE": "2020-06-13", 50 | "SITE": "AFETR", 51 | "DECAY_DATE": null, 52 | "DECAYED": 0, 53 | "FILE": 2768947, 54 | "GP_ID": 155985688, 55 | "TLE_LINE0": "0 STARLINK-1506", 56 | "TLE_LINE1": "1 45747U 20038T 20171.83334491 .03503094 12654-1 10068-1 0 9995", 57 | "TLE_LINE2": "2 45747 53.0017 266.3302 0087515 69.9474 221.4733 15.88829743 2124" 58 | }, 59 | "version": "v1.0", 60 | "launch": "5eb87d46ffd86e000604b389", 61 | "longitude": 165.93047730624068, 62 | "latitude": -52.91311434465077, 63 | "height_km": 446.61936740361125, 64 | "velocity_kms": 7.643507427834188, 65 | "id": "5eed7716096e590006985825" 66 | } 67 | ... 68 | ] 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/v4/starlink/create.md: -------------------------------------------------------------------------------- 1 | # Create a Starlink satellite 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/starlink` 6 | 7 | **Auth required** : `True` 8 | 9 | **Body** : 10 | 11 | ```json 12 | { 13 | "spaceTrack": { 14 | "CCSDS_OMM_VERS": "2.0", 15 | "COMMENT": "GENERATED VIA SPACE-TRACK.ORG API", 16 | "CREATION_DATE": "2020-06-19 21:46:09", 17 | "ORIGINATOR": "18 SPCS", 18 | "OBJECT_NAME": "STARLINK-1506", 19 | "OBJECT_ID": "2020-038T", 20 | "CENTER_NAME": "EARTH", 21 | "REF_FRAME": "TEME", 22 | "TIME_SYSTEM": "UTC", 23 | "MEAN_ELEMENT_THEORY": "SGP4", 24 | "EPOCH": "2020-06-19 20:00:01.000224", 25 | "MEAN_MOTION": 15.88829743, 26 | "ECCENTRICITY": 0.0087515, 27 | "INCLINATION": 53.002, 28 | "RA_OF_ASC_NODE": 266.3302, 29 | "ARG_OF_PERICENTER": 69.9474, 30 | "MEAN_ANOMALY": 221.4733, 31 | "EPHEMERIS_TYPE": 0, 32 | "CLASSIFICATION_TYPE": "U", 33 | "NORAD_CAT_ID": 45747, 34 | "ELEMENT_SET_NO": 999, 35 | "REV_AT_EPOCH": 212, 36 | "BSTAR": 0.01007, 37 | "MEAN_MOTION_DOT": 0.03503094, 38 | "MEAN_MOTION_DDOT": 0.01265, 39 | "SEMIMAJOR_AXIS": 6683.699, 40 | "PERIOD": 90.632, 41 | "APOAPSIS": 364.057, 42 | "PERIAPSIS": 247.072, 43 | "OBJECT_TYPE": "PAYLOAD", 44 | "RCS_SIZE": null, 45 | "COUNTRY_CODE": "US", 46 | "LAUNCH_DATE": "2020-06-13", 47 | "SITE": "AFETR", 48 | "DECAY_DATE": null, 49 | "DECAYED": 0, 50 | "FILE": 2768947, 51 | "GP_ID": 155985688, 52 | "TLE_LINE0": "0 STARLINK-1506", 53 | "TLE_LINE1": "1 45747U 20038T 20171.83334491 .03503094 12654-1 10068-1 0 9995", 54 | "TLE_LINE2": "2 45747 53.0017 266.3302 0087515 69.9474 221.4733 15.88829743 2124" 55 | }, 56 | "version": "v1.0", 57 | "launch": "5eb87d46ffd86e000604b389" 58 | } 59 | ``` 60 | 61 | ## Success Response 62 | 63 | **Code** : `201 Created` 64 | 65 | **Content example** : `Created` 66 | 67 | ## Error Responses 68 | 69 | **Code** : `400 Bad Request` 70 | 71 | **Content** : Mongoose error is shown, with suggestions to fix the query. 72 | -------------------------------------------------------------------------------- /docs/v4/starlink/delete.md: -------------------------------------------------------------------------------- 1 | # Delete a Starlink satellite 2 | 3 | **Method** : `DELETE` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/starlink/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the Starlink satellite 8 | 9 | **Auth required** : `True` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : `OK` 16 | 17 | ## Error Responses 18 | 19 | **Code** : `400 Bad Request` 20 | 21 | **Content** : Mongoose error is shown, with suggestions to fix the query. 22 | -------------------------------------------------------------------------------- /docs/v4/starlink/one.md: -------------------------------------------------------------------------------- 1 | # Get one Starlink satellite 2 | 3 | **Method** : `GET` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/starlink/:id` 6 | 7 | **URL Parameters** : `id=[string]` where `id` is the ID of the Starlink sat 8 | 9 | **Auth required** : `False` 10 | 11 | ## Success Response 12 | 13 | **Code** : `200 OK` 14 | 15 | **Content example** : 16 | 17 | ```json 18 | { 19 | "spaceTrack": { 20 | "CCSDS_OMM_VERS": "2.0", 21 | "COMMENT": "GENERATED VIA SPACE-TRACK.ORG API", 22 | "CREATION_DATE": "2020-06-19 21:46:09", 23 | "ORIGINATOR": "18 SPCS", 24 | "OBJECT_NAME": "STARLINK-1506", 25 | "OBJECT_ID": "2020-038T", 26 | "CENTER_NAME": "EARTH", 27 | "REF_FRAME": "TEME", 28 | "TIME_SYSTEM": "UTC", 29 | "MEAN_ELEMENT_THEORY": "SGP4", 30 | "EPOCH": "2020-06-19 20:00:01.000224", 31 | "MEAN_MOTION": 15.88829743, 32 | "ECCENTRICITY": 0.0087515, 33 | "INCLINATION": 53.002, 34 | "RA_OF_ASC_NODE": 266.3302, 35 | "ARG_OF_PERICENTER": 69.9474, 36 | "MEAN_ANOMALY": 221.4733, 37 | "EPHEMERIS_TYPE": 0, 38 | "CLASSIFICATION_TYPE": "U", 39 | "NORAD_CAT_ID": 45747, 40 | "ELEMENT_SET_NO": 999, 41 | "REV_AT_EPOCH": 212, 42 | "BSTAR": 0.01007, 43 | "MEAN_MOTION_DOT": 0.03503094, 44 | "MEAN_MOTION_DDOT": 0.01265, 45 | "SEMIMAJOR_AXIS": 6683.699, 46 | "PERIOD": 90.632, 47 | "APOAPSIS": 364.057, 48 | "PERIAPSIS": 247.072, 49 | "OBJECT_TYPE": "PAYLOAD", 50 | "RCS_SIZE": null, 51 | "COUNTRY_CODE": "US", 52 | "LAUNCH_DATE": "2020-06-13", 53 | "SITE": "AFETR", 54 | "DECAY_DATE": null, 55 | "DECAYED": 0, 56 | "FILE": 2768947, 57 | "GP_ID": 155985688, 58 | "TLE_LINE0": "0 STARLINK-1506", 59 | "TLE_LINE1": "1 45747U 20038T 20171.83334491 .03503094 12654-1 10068-1 0 9995", 60 | "TLE_LINE2": "2 45747 53.0017 266.3302 0087515 69.9474 221.4733 15.88829743 2124" 61 | }, 62 | "version": "v1.0", 63 | "launch": "5eb87d46ffd86e000604b389", 64 | "longitude": 165.93047730624068, 65 | "latitude": -52.91311434465077, 66 | "height_km": 446.61936740361125, 67 | "velocity_kms": 7.643507427834188, 68 | "id": "5eed7716096e590006985825" 69 | } 70 | ``` 71 | 72 | ## Error Responses 73 | 74 | **Code** : `404 NOT FOUND` 75 | 76 | **Content** : `Not Found` 77 | -------------------------------------------------------------------------------- /docs/v4/starlink/query.md: -------------------------------------------------------------------------------- 1 | # Query Starlink satellites 2 | 3 | **Method** : `POST` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/starlink/query` 6 | 7 | **Auth required** : `False` 8 | 9 | **Body** : 10 | 11 | See [query](../queries.md) guide for more details on building queries and paginating results. 12 | 13 | ```json 14 | { 15 | "query": {}, 16 | "options": {} 17 | } 18 | ``` 19 | 20 | ## Success Response 21 | 22 | **Code** : `200 OK` 23 | 24 | **Content example** : 25 | 26 | ```json 27 | { 28 | "docs": [ 29 | { 30 | "spaceTrack": { 31 | "CCSDS_OMM_VERS": "2.0", 32 | "COMMENT": "GENERATED VIA SPACE-TRACK.ORG API", 33 | "CREATION_DATE": "2020-06-19 21:36:08", 34 | "ORIGINATOR": "18 SPCS", 35 | "OBJECT_NAME": "STARLINK-30", 36 | "OBJECT_ID": "2019-029K", 37 | "CENTER_NAME": "EARTH", 38 | "REF_FRAME": "TEME", 39 | "TIME_SYSTEM": "UTC", 40 | "MEAN_ELEMENT_THEORY": "SGP4", 41 | "EPOCH": "2020-06-19 20:00:01.000224", 42 | "MEAN_MOTION": 15.43862877, 43 | "ECCENTRICITY": 0.000125, 44 | "INCLINATION": 52.996, 45 | "RA_OF_ASC_NODE": 195.8544, 46 | "ARG_OF_PERICENTER": 108.6906, 47 | "MEAN_ANOMALY": 109.3199, 48 | "EPHEMERIS_TYPE": 0, 49 | "CLASSIFICATION_TYPE": "U", 50 | "NORAD_CAT_ID": 44244, 51 | "ELEMENT_SET_NO": 999, 52 | "REV_AT_EPOCH": 5947, 53 | "BSTAR": 0.00007, 54 | "MEAN_MOTION_DOT": 0.00002829, 55 | "MEAN_MOTION_DDOT": 0, 56 | "SEMIMAJOR_AXIS": 6812.858, 57 | "PERIOD": 93.272, 58 | "APOAPSIS": 435.574, 59 | "PERIAPSIS": 433.871, 60 | "OBJECT_TYPE": "PAYLOAD", 61 | "RCS_SIZE": "LARGE", 62 | "COUNTRY_CODE": "US", 63 | "LAUNCH_DATE": "2019-05-24", 64 | "SITE": "AFETR", 65 | "DECAY_DATE": null, 66 | "DECAYED": 0, 67 | "FILE": 2768931, 68 | "GP_ID": 155985469, 69 | "TLE_LINE0": "0 STARLINK-30", 70 | "TLE_LINE1": "1 44244U 19029K 20171.83334491 .00002829 00000-0 70479-4 0 9997", 71 | "TLE_LINE2": "2 44244 52.9964 195.8544 0001250 108.6906 109.3199 15.43862877 59477" 72 | }, 73 | "version": "v0.9", 74 | "launch": "5eb87d30ffd86e000604b378", 75 | "longitude": 10.551678198548517, 76 | "latitude": 8.26018124742001, 77 | "height_km": 434.5577668080887, 78 | "velocity_kms": 7.653046786650296, 79 | "id": "5eed770f096e59000698560d" 80 | }, 81 | ... 82 | ], 83 | "totalDocs": 537, 84 | "offset": 0, 85 | "limit": 10, 86 | "totalPages": 54, 87 | "page": 1, 88 | "pagingCounter": 1, 89 | "hasPrevPage": false, 90 | "hasNextPage": true, 91 | "prevPage": null, 92 | "nextPage": 2 93 | } 94 | ``` 95 | 96 | ## Error Responses 97 | 98 | **Code** : `400 Bad Request` 99 | 100 | **Content** : Mongoose error is shown, with suggestions to fix the query. 101 | -------------------------------------------------------------------------------- /docs/v4/starlink/update.md: -------------------------------------------------------------------------------- 1 | # Update a Starlink satellite 2 | 3 | **Method** : `PATCH` 4 | 5 | **URL** : `https://api.spacexdata.com/v4/starlink/:norad_id` 6 | 7 | **URL Parameters** : `norad_id=[string]` where `norad_id` is the Norad Id of the Starlink sat 8 | 9 | **Auth required** : `True` 10 | 11 | **Body** : 12 | 13 | ```json 14 | { 15 | "version": "v1.0", 16 | } 17 | ``` 18 | 19 | ## Success Response 20 | 21 | **Code** : `200 OK` 22 | 23 | **Content example** : `OK` 24 | 25 | ## Error Responses 26 | 27 | **Code** : `400 Bad Request` 28 | 29 | **Content** : Mongoose error is shown, with suggestions to fix the query. 30 | -------------------------------------------------------------------------------- /jobs/capsules.js: -------------------------------------------------------------------------------- 1 | const got = require('got'); 2 | const { logger } = require('../middleware/logger'); 3 | 4 | const SPACEX_API = 'https://api.spacexdata.com/v4'; 5 | const KEY = process.env.SPACEX_KEY; 6 | const HEALTHCHECK = process.env.CAPSULES_HEALTHCHECK; 7 | 8 | /** 9 | * Update capsule landings/reuse count 10 | * @return {Promise} 11 | */ 12 | module.exports = async () => { 13 | try { 14 | const capsules = await got.post(`${SPACEX_API}/capsules/query`, { 15 | json: { 16 | options: { 17 | pagination: false, 18 | }, 19 | }, 20 | resolveBodyOnly: true, 21 | responseType: 'json', 22 | }); 23 | 24 | const updates = capsules.docs.map(async (capsule) => { 25 | const waterLandings = await got.post(`${SPACEX_API}/payloads/query`, { 26 | json: { 27 | query: { 28 | 'dragon.capsule': capsule.id, 29 | 'dragon.water_landing': true, 30 | }, 31 | options: { 32 | pagination: false, 33 | }, 34 | }, 35 | resolveBodyOnly: true, 36 | responseType: 'json', 37 | }); 38 | 39 | const landLandings = await got.post(`${SPACEX_API}/payloads/query`, { 40 | json: { 41 | query: { 42 | 'dragon.capsule': capsule.id, 43 | 'dragon.land_landing': true, 44 | }, 45 | options: { 46 | pagination: false, 47 | }, 48 | }, 49 | resolveBodyOnly: true, 50 | responseType: 'json', 51 | }); 52 | 53 | await got.patch(`${SPACEX_API}/capsules/${capsule.id}`, { 54 | json: { 55 | reuse_count: capsule.launches.length, 56 | water_landings: waterLandings.totalDocs, 57 | land_landings: landLandings.totalDocs, 58 | }, 59 | headers: { 60 | 'spacex-key': KEY, 61 | }, 62 | }); 63 | }); 64 | 65 | await Promise.all(updates); 66 | 67 | logger.info('Capsules updated'); 68 | 69 | if (HEALTHCHECK) { 70 | await got(HEALTHCHECK); 71 | } 72 | } catch (error) { 73 | console.log(error); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /jobs/landpads.js: -------------------------------------------------------------------------------- 1 | const got = require('got'); 2 | const { logger } = require('../middleware/logger'); 3 | 4 | const SPACEX_API = 'https://api.spacexdata.com/v4'; 5 | const KEY = process.env.SPACEX_KEY; 6 | const HEALTHCHECK = process.env.LANDPADS_HEALTHCHECK; 7 | 8 | /** 9 | * Update landpad attempts/successes 10 | * @return {Promise} 11 | */ 12 | module.exports = async () => { 13 | try { 14 | const landpads = await got.post(`${SPACEX_API}/landpads/query`, { 15 | json: { 16 | options: { 17 | pagination: false, 18 | }, 19 | }, 20 | resolveBodyOnly: true, 21 | responseType: 'json', 22 | }); 23 | 24 | const updates = landpads.docs.map(async (landpad) => { 25 | const attempts = await got.post(`${SPACEX_API}/launches/query`, { 26 | json: { 27 | query: { 28 | cores: { 29 | $elemMatch: { 30 | landpad: landpad.id, 31 | landing_attempt: true, 32 | }, 33 | }, 34 | upcoming: false, 35 | success: true, 36 | }, 37 | options: { 38 | pagination: false, 39 | }, 40 | }, 41 | resolveBodyOnly: true, 42 | responseType: 'json', 43 | }); 44 | 45 | const successes = await got.post(`${SPACEX_API}/launches/query`, { 46 | json: { 47 | query: { 48 | cores: { 49 | $elemMatch: { 50 | landpad: landpad.id, 51 | landing_attempt: true, 52 | landing_success: true, 53 | }, 54 | }, 55 | upcoming: false, 56 | success: true, 57 | }, 58 | options: { 59 | pagination: false, 60 | }, 61 | }, 62 | resolveBodyOnly: true, 63 | responseType: 'json', 64 | }); 65 | 66 | await got.patch(`${SPACEX_API}/landpads/${landpad.id}`, { 67 | json: { 68 | landing_attempts: attempts.totalDocs, 69 | landing_successes: successes.totalDocs, 70 | }, 71 | headers: { 72 | 'spacex-key': KEY, 73 | }, 74 | }); 75 | }); 76 | 77 | await Promise.all(updates); 78 | 79 | logger.info('Landpads updated'); 80 | 81 | if (HEALTHCHECK) { 82 | await got(HEALTHCHECK); 83 | } 84 | } catch (error) { 85 | console.log(error); 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /jobs/launchpads.js: -------------------------------------------------------------------------------- 1 | const got = require('got'); 2 | const { logger } = require('../middleware/logger'); 3 | 4 | const SPACEX_API = 'https://api.spacexdata.com/v4'; 5 | const KEY = process.env.SPACEX_KEY; 6 | const HEALTHCHECK = process.env.LAUNCHPADS_HEALTHCHECK; 7 | 8 | /** 9 | * Update launchpad attempts/successes 10 | * @return {Promise} 11 | */ 12 | module.exports = async () => { 13 | try { 14 | const launchpads = await got.post(`${SPACEX_API}/launchpads/query`, { 15 | json: { 16 | options: { 17 | pagination: false, 18 | }, 19 | }, 20 | resolveBodyOnly: true, 21 | responseType: 'json', 22 | }); 23 | 24 | const updates = launchpads.docs.map(async (launchpad) => { 25 | const attempts = await got.post(`${SPACEX_API}/launches/query`, { 26 | json: { 27 | query: { 28 | launchpad: launchpad.id, 29 | upcoming: false, 30 | }, 31 | options: { 32 | pagination: false, 33 | }, 34 | }, 35 | resolveBodyOnly: true, 36 | responseType: 'json', 37 | }); 38 | 39 | const successes = await got.post(`${SPACEX_API}/launches/query`, { 40 | json: { 41 | query: { 42 | launchpad: launchpad.id, 43 | upcoming: false, 44 | success: true, 45 | }, 46 | options: { 47 | pagination: false, 48 | }, 49 | }, 50 | resolveBodyOnly: true, 51 | responseType: 'json', 52 | }); 53 | 54 | await got.patch(`${SPACEX_API}/launchpads/${launchpad.id}`, { 55 | json: { 56 | launch_attempts: attempts.totalDocs, 57 | launch_successes: successes.totalDocs, 58 | }, 59 | headers: { 60 | 'spacex-key': KEY, 61 | }, 62 | }); 63 | }); 64 | 65 | await Promise.all(updates); 66 | 67 | logger.info('Launchpads updated'); 68 | 69 | if (HEALTHCHECK) { 70 | await got(HEALTHCHECK); 71 | } 72 | } catch (error) { 73 | console.log(error); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /jobs/payloads.js: -------------------------------------------------------------------------------- 1 | const got = require('got'); 2 | const { CookieJar } = require('tough-cookie'); 3 | const { logger } = require('../middleware/logger'); 4 | 5 | const SPACEX_API = 'https://api.spacexdata.com/v4'; 6 | const KEY = process.env.SPACEX_KEY; 7 | const HEALTHCHECK = process.env.PAYLOADS_HEALTHCHECK; 8 | 9 | /** 10 | * Update payload orbit params 11 | * @return {Promise} 12 | */ 13 | module.exports = async () => { 14 | try { 15 | const payloads = await got.post(`${SPACEX_API}/payloads/query`, { 16 | json: { 17 | query: {}, 18 | options: { 19 | pagination: false, 20 | }, 21 | }, 22 | resolveBodyOnly: true, 23 | responseType: 'json', 24 | }); 25 | 26 | const cookieJar = new CookieJar(); 27 | 28 | await got.post('https://www.space-track.org/ajaxauth/login', { 29 | form: { 30 | identity: process.env.SPACEX_TRACK_LOGIN, 31 | password: process.env.SPACEX_TRACK_PASSWORD, 32 | }, 33 | cookieJar, 34 | }); 35 | 36 | // eslint-disable-next-line no-secrets/no-secrets 37 | const data = await got('https://www.space-track.org/basicspacedata/query/class/tle_latest/ORDINAL/1/orderby/NORAD_CAT_ID/epoch/>now-45/format/json', { 38 | resolveBodyOnly: true, 39 | responseType: 'json', 40 | cookieJar, 41 | }); 42 | 43 | const updates = payloads.docs.map(async (payload) => { 44 | const noradId = payload.norad_ids.shift() || null; 45 | const specificOrbit = data.find((sat) => parseInt(sat.NORAD_CAT_ID, 10) === noradId); 46 | if (specificOrbit) { 47 | await got.patch(`${SPACEX_API}/payloads/${payload.id}`, { 48 | json: { 49 | epoch: new Date(Date.parse(specificOrbit.EPOCH)).toISOString(), 50 | mean_motion: parseFloat(specificOrbit.MEAN_MOTION), 51 | raan: parseFloat(specificOrbit.RA_OF_ASC_NODE), 52 | arg_of_pericenter: parseFloat(specificOrbit.ARG_OF_PERICENTER), 53 | mean_anomaly: parseFloat(specificOrbit.MEAN_ANOMALY), 54 | semi_major_axis_km: parseFloat(specificOrbit.SEMIMAJOR_AXIS), 55 | eccentricity: parseFloat(specificOrbit.ECCENTRICITY), 56 | periapsis_km: parseFloat(specificOrbit.PERIGEE), 57 | apoapsis_km: parseFloat(specificOrbit.APOGEE), 58 | inclination_deg: parseFloat(specificOrbit.INCLINATION), 59 | period_min: parseFloat(specificOrbit.PERIOD), 60 | }, 61 | headers: { 62 | 'spacex-key': KEY, 63 | }, 64 | }); 65 | } 66 | }); 67 | 68 | await Promise.all(updates); 69 | 70 | logger.info({ 71 | orbitsUpdated: true, 72 | }); 73 | 74 | if (HEALTHCHECK) { 75 | await got(HEALTHCHECK); 76 | } 77 | } catch (error) { 78 | console.log(error); 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /jobs/webcast.js: -------------------------------------------------------------------------------- 1 | const got = require('got'); 2 | const fuzz = require('fuzzball'); 3 | const { logger } = require('../middleware/logger'); 4 | 5 | const YOUTUBE_PREFIX = 'https://youtu.be'; 6 | const SPACEX_API = 'https://api.spacexdata.com/v4'; 7 | const CHANNEL_ID = 'UCtI0Hodo5o5dUb67FeUjDeA'; 8 | const { 9 | SPACEX_KEY, 10 | YOUTUBE_KEY, 11 | WEBCAST_HEALTHCHECK, 12 | } = process.env; 13 | 14 | /** 15 | * Check for new SpaceX webcast links 16 | * @return {Promise} 17 | */ 18 | module.exports = async () => { 19 | try { 20 | // Check if any upcoming streams on youtube 21 | const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=${CHANNEL_ID}&eventType=upcoming&maxResults=1&type=video&key=${YOUTUBE_KEY}`; 22 | const upcomingStreams = await got(url, { 23 | resolveBodyOnly: true, 24 | responseType: 'json', 25 | }); 26 | 27 | if (upcomingStreams?.items?.length === 1) { 28 | const launches = await got.post(`${SPACEX_API}/launches/query`, { 29 | json: { 30 | query: { 31 | upcoming: true, 32 | }, 33 | options: { 34 | sort: { 35 | flight_number: 'asc', 36 | }, 37 | limit: 1, 38 | }, 39 | }, 40 | resolveBodyOnly: true, 41 | responseType: 'json', 42 | }); 43 | const launchId = launches.docs[0].id; 44 | const missionName = launches.docs[0].name; 45 | const youtubeTitle = upcomingStreams.items[0].snippet.title; 46 | const youtubeId = upcomingStreams.items[0].id.videoId; 47 | 48 | // Fuzzy check video title to make sure it's at least related to the launch 49 | const ratio = fuzz.ratio(youtubeTitle, missionName); 50 | if (ratio >= 50) { 51 | await got.patch(`${SPACEX_API}/launches/${launchId}`, { 52 | json: { 53 | 'links.webcast': `${YOUTUBE_PREFIX}/${youtubeId}`, 54 | 'links.youtube_id': youtubeId, 55 | }, 56 | headers: { 57 | 'spacex-key': SPACEX_KEY, 58 | }, 59 | }); 60 | logger.info({ 61 | rawMission: youtubeTitle, 62 | apiMission: missionName, 63 | url: `${YOUTUBE_PREFIX}/${youtubeId}`, 64 | matchRatio: ratio, 65 | match: true, 66 | }); 67 | } else { 68 | logger.info({ 69 | rawMission: youtubeTitle, 70 | apiMission: missionName, 71 | url: `${YOUTUBE_PREFIX}/${youtubeId}`, 72 | matchRatio: ratio, 73 | match: false, 74 | }); 75 | } 76 | } else { 77 | logger.info({ 78 | match: false, 79 | }); 80 | } 81 | 82 | if (WEBCAST_HEALTHCHECK) { 83 | await got(WEBCAST_HEALTHCHECK); 84 | } 85 | } catch (error) { 86 | console.log(error); 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /jobs/worker.js: -------------------------------------------------------------------------------- 1 | const { CronJob } = require('cron'); 2 | const launches = require('./launches'); 3 | const payloads = require('./payloads'); 4 | const landpads = require('./landpads'); 5 | const launchpads = require('./launchpads'); 6 | const capsules = require('./capsules'); 7 | const cores = require('./cores'); 8 | const roadster = require('./roadster'); 9 | const upcoming = require('./upcoming'); 10 | const starlink = require('./starlink'); 11 | const webcast = require('./webcast'); 12 | 13 | // Every 10 minutes 14 | const launchesJob = new CronJob('*/10 * * * *', launches); 15 | 16 | // Every 10 minutes 17 | const landpadsJob = new CronJob('*/10 * * * *', landpads); 18 | 19 | // Every 10 minutes 20 | const launchpadsJob = new CronJob('*/10 * * * *', launchpads); 21 | 22 | // Every 10 minutes 23 | const capsulesJob = new CronJob('*/10 * * * *', capsules); 24 | 25 | // Every 10 minutes 26 | const coresJob = new CronJob('*/10 * * * *', cores); 27 | 28 | // Every 10 minutes 29 | const roadsterJob = new CronJob('*/10 * * * *', roadster); 30 | 31 | // Every 10 minutes 32 | const upcomingJob = new CronJob('*/10 * * * *', upcoming); 33 | 34 | // Every hour on :25 35 | const payloadsJob = new CronJob('25 * * * *', payloads); 36 | 37 | // Every hour on :35 38 | const starlinkJob = new CronJob('35 * * * *', starlink); 39 | 40 | // Every hour on :45 41 | const webcastJob = new CronJob('45 * * * *', webcast); 42 | 43 | launchesJob.start(); 44 | payloadsJob.start(); 45 | landpadsJob.start(); 46 | launchpadsJob.start(); 47 | capsulesJob.start(); 48 | coresJob.start(); 49 | roadsterJob.start(); 50 | upcomingJob.start(); 51 | starlinkJob.start(); 52 | webcastJob.start(); 53 | -------------------------------------------------------------------------------- /middleware/auth.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const db = mongoose.connection.useDb('auth'); 4 | 5 | /** 6 | * Authentication middleware 7 | */ 8 | module.exports = async (ctx, next) => { 9 | const key = ctx.request.headers['spacex-key']; 10 | if (key) { 11 | const user = await db.collection('users').findOne({ key }); 12 | if (user && user.key === key) { 13 | ctx.state.role = user.role; 14 | await next(); 15 | return; 16 | } 17 | } 18 | ctx.status = 401; 19 | ctx.body = 'https://youtu.be/RfiQYRn7fBg'; 20 | }; 21 | -------------------------------------------------------------------------------- /middleware/authz/index.js: -------------------------------------------------------------------------------- 1 | const { newEnforcer } = require('casbin'); 2 | 3 | /** 4 | * Authorization middleware 5 | */ 6 | module.exports = async (ctx, next) => { 7 | const { role } = ctx.state; 8 | const { path, method } = ctx; 9 | const enforcer = await newEnforcer(`${__dirname}/model.conf`, `${__dirname}/policy.csv`); 10 | const allowed = await enforcer.enforce(role, path, method); 11 | if (allowed) { 12 | await next(); 13 | return; 14 | } 15 | ctx.status = 403; 16 | }; 17 | -------------------------------------------------------------------------------- /middleware/authz/model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && (r.act == p.act || p.act == "*") -------------------------------------------------------------------------------- /middleware/authz/policy.csv: -------------------------------------------------------------------------------- 1 | p, root, /*, * 2 | 3 | p, create:capsule, /v4/capsules, POST 4 | p, update:capsule, /v4/capsules/:id, PATCH 5 | p, delete:capsule, /v4/capsules/:id, DELETE 6 | 7 | p, create:cores, /v4/cores, POST 8 | p, update:cores, /v4/cores/:id, PATCH 9 | p, delete:cores, /v4/cores/:id, DELETE 10 | 11 | p, create:crew, /v4/crew, POST 12 | p, update:crew, /v4/crew/:id, PATCH 13 | p, delete:crew, /v4/crew/:id, DELETE 14 | 15 | p, create:dragons, /v4/dragons, POST 16 | p, update:dragons, /v4/dragons/:id, PATCH 17 | p, delete:dragons, /v4/dragons/:id, DELETE 18 | 19 | p, create:landpads, /v4/landpads, POST 20 | p, update:landpads, /v4/landpads/:id, PATCH 21 | p, delete:landpads, /v4/landpads/:id, DELETE 22 | 23 | p, create:launches, /v4/launches, POST 24 | p, update:launches, /v4/launches/:id, PATCH 25 | p, delete:launches, /v4/launches/:id, DELETE 26 | 27 | p, create:launchpads, /v4/launchpads, POST 28 | p, update:launchpads, /v4/launchpads/:id, PATCH 29 | p, delete:launchpads, /v4/launchpads/:id, DELETE 30 | 31 | p, create:payloads, /v4/payloads, POST 32 | p, update:payloads, /v4/payloads/:id, PATCH 33 | p, delete:payloads, /v4/payloads/:id, DELETE 34 | 35 | p, create:rockets, /v4/rockets, POST 36 | p, update:rockets, /v4/rockets/:id, PATCH 37 | p, delete:rockets, /v4/rockets/:id, DELETE 38 | 39 | p, create:ships, /v4/ships, POST 40 | p, update:ships, /v4/ships/:id, PATCH 41 | p, delete:ships, /v4/ships/:id, DELETE 42 | 43 | p, update:company, /v4/company/:id, PATCH 44 | 45 | p, update:roadster, /v4/roadster/:id, PATCH 46 | 47 | p, create:starlink, /v4/starlink, POST 48 | p, update:starlink, /v4/starlink/:norad_id, PATCH 49 | p, delete:starlink, /v4/starlink/:id, DELETE 50 | 51 | p, get:user, /v4/users, GET 52 | p, get-one:user, /v4/users/:id, GET 53 | p, query:user, /v4/users/query, POST 54 | p, create:user, /v4/users, POST 55 | p, update:user, /v4/users/:id, PATCH 56 | p, delete:user, /v4/users/:id, DELETE 57 | 58 | p, clear-cache, /v4/admin/cache, DELETE 59 | 60 | g, superuser, root 61 | 62 | g, create, create:capsule 63 | g, update, update:capsule 64 | g, delete, delete:capsule 65 | 66 | g, create, create:cores 67 | g, update, update:cores 68 | g, delete, delete:cores 69 | 70 | g, create, create:crew 71 | g, update, update:crew 72 | g, delete, delete:crew 73 | 74 | g, create, create:dragons 75 | g, update, update:dragons 76 | g, delete, delete:dragons 77 | 78 | g, create, create:landpads 79 | g, update, update:landpads 80 | g, delete, delete:landpads 81 | 82 | g, create, create:launches 83 | g, update, update:launches 84 | g, delete, delete:launches 85 | 86 | g, create, create:launchpads 87 | g, update, update:launchpads 88 | g, delete, delete:launchpads 89 | 90 | g, create, create:payloads 91 | g, update, update:payloads 92 | g, delete, delete:payloads 93 | 94 | g, create, create:rockets 95 | g, update, update:rockets 96 | g, delete, delete:rockets 97 | 98 | g, create, create:ships 99 | g, update, update:ships 100 | g, delete, delete:ships 101 | 102 | g, update, update:company 103 | 104 | g, update, update:roadster 105 | 106 | g, create, create:starlink 107 | g, update, update:starlink 108 | g, delete, delete:starlink 109 | 110 | g, user, create 111 | g, user, update 112 | g, user, delete -------------------------------------------------------------------------------- /middleware/cache.js: -------------------------------------------------------------------------------- 1 | const Redis = require('ioredis'); 2 | const blake3 = require('blake3'); 3 | const { logger } = require('./logger'); 4 | 5 | let redis; 6 | let redisAvailable = false; 7 | 8 | if (process.env.SPACEX_REDIS) { 9 | redis = new Redis(process.env.SPACEX_REDIS); 10 | } else { 11 | redis = new Redis(); 12 | } 13 | 14 | redis.on('error', () => { 15 | redisAvailable = false; 16 | }); 17 | redis.on('end', () => { 18 | redisAvailable = false; 19 | }); 20 | redis.on('ready', () => { 21 | redisAvailable = true; 22 | logger.info('Redis connected'); 23 | }); 24 | 25 | /** 26 | * BLAKE3 hash func for redis keys 27 | * 28 | * @param {String} str String to hash 29 | * @returns {String} Hashed result 30 | */ 31 | const hash = (str) => blake3.createHash().update(str).digest('hex'); 32 | 33 | /** 34 | * Redis cache middleware 35 | * 36 | * @param {Number} ttl Cache TTL in seconds 37 | * @returns {void} 38 | */ 39 | module.exports = (ttl) => async (ctx, next) => { 40 | if (process.env.NODE_ENV !== 'production') { 41 | await next(); 42 | return; 43 | } 44 | 45 | if (!redisAvailable) { 46 | ctx.response.set('spacex-api-cache-online', 'false'); 47 | await next(); 48 | return; 49 | } 50 | ctx.response.set('spacex-api-cache-online', 'true'); 51 | 52 | const { url, method } = ctx.request; 53 | const key = `spacex-cache:${hash(`${method}${url}${JSON.stringify(ctx.request.body)}`)}`; 54 | 55 | if (ttl) { 56 | ctx.response.set('Cache-Control', `max-age=${ttl}`); 57 | } else { 58 | ctx.response.set('Cache-Control', 'no-store'); 59 | } 60 | 61 | // Try and get cache 62 | if (ctx.request.method !== 'GET' || ctx.request.method !== 'POST') { 63 | let cached; 64 | try { 65 | cached = await redis.get(key); 66 | if (cached) { 67 | ctx.response.status = 200; 68 | ctx.response.set('spacex-api-cache', 'HIT'); 69 | ctx.response.type = 'application/json'; 70 | ctx.response.body = cached; 71 | cached = true; 72 | } 73 | } catch (e) { 74 | cached = false; 75 | } 76 | if (cached) { 77 | return; 78 | } 79 | await next(); 80 | 81 | const responseBody = JSON.stringify(ctx.response.body); 82 | ctx.response.set('spacex-api-cache', 'MISS'); 83 | 84 | // Set cache 85 | try { 86 | if ((ctx.response.status !== 200) || !responseBody) { 87 | return; 88 | } 89 | await redis.set(key, responseBody, 'EX', ttl); 90 | } catch (e) { 91 | console.log(`Failed to set cache: ${e.message}`); 92 | } 93 | } 94 | }; 95 | 96 | // Share redis connection 97 | Object.defineProperty(module.exports, 'redis', { 98 | value: redis, 99 | writable: false, 100 | }); 101 | -------------------------------------------------------------------------------- /middleware/errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Error handler middleware 3 | * 4 | * @param {Object} ctx Koa context 5 | * @param {function} next Koa next function 6 | * @returns {void} 7 | */ 8 | module.exports = async (ctx, next) => { 9 | try { 10 | await next(); 11 | } catch (err) { 12 | if (err.kind === 'ObjectId') { 13 | err.status = 404; 14 | } else { 15 | ctx.status = err.status || 500; 16 | ctx.body = err.message; 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /middleware/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Export middleware 3 | */ 4 | module.exports.auth = require('./auth'); 5 | module.exports.authz = require('./authz'); 6 | module.exports.cache = require('./cache'); 7 | module.exports.errors = require('./errors'); 8 | module.exports.responseTime = require('./response-time'); 9 | module.exports.logger = require('./logger'); 10 | -------------------------------------------------------------------------------- /middleware/logger.js: -------------------------------------------------------------------------------- 1 | const koaPino = require('koa-pino-logger'); 2 | const pino = require('pino'); 3 | 4 | const devOpts = { 5 | prettyPrint: true, 6 | }; 7 | 8 | let requestLog; 9 | let logger; 10 | 11 | if (process.env.NODE_ENV === 'production') { 12 | requestLog = koaPino(); 13 | logger = pino(); 14 | } else { 15 | requestLog = koaPino(devOpts); 16 | logger = pino(devOpts); 17 | } 18 | 19 | module.exports.requestLogger = requestLog; 20 | module.exports.logger = logger; 21 | -------------------------------------------------------------------------------- /middleware/response-time.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Return header with response time 3 | * 4 | * @param {Object} ctx Koa context 5 | * @param {Function} next Next middleware 6 | * @returns {Promise} 7 | */ 8 | module.exports = async (ctx, next) => { 9 | const start = Date.now(); 10 | await next(); 11 | const ms = Date.now() - start; 12 | ctx.set('spacex-api-response-time', `${ms}ms`); 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spacex-api", 3 | "version": "4.0.0", 4 | "description": "Open Source REST API for data about SpaceX", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "npm run lint && npm run check-dependencies && jest --silent --verbose", 8 | "start": "node server.js", 9 | "worker": "node jobs/worker.js", 10 | "lint": "eslint .", 11 | "check-dependencies": "npx depcheck --ignores=\"pino-pretty\"" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/r-spacex/SpaceX-API" 16 | }, 17 | "keywords": [ 18 | "spacex", 19 | "space", 20 | "rocket", 21 | "rest-api", 22 | "mongodb", 23 | "koa" 24 | ], 25 | "author": "Jake Meyer ", 26 | "license": "Apache-2.0", 27 | "bugs": { 28 | "url": "https://github.com/r-spacex/SpaceX-API/issues" 29 | }, 30 | "homepage": "https://github.com/r-spacex/SpaceX-API", 31 | "dependencies": { 32 | "blake3": "^2.1.7", 33 | "cron": "^3.1.7", 34 | "fuzzball": "^2.1.2", 35 | "got": "^14.2.1", 36 | "ioredis": "^5.4.1", 37 | "koa": "^2.15.3", 38 | "koa-bodyparser": "^4.4.1", 39 | "koa-conditional-get": "^3.0.0", 40 | "koa-etag": "^4.0.0", 41 | "koa-helmet": "^7.0.2", 42 | "koa-pino-logger": "^4.0.0", 43 | "koa-router": "^12.0.1", 44 | "koa2-cors": "^2.0.6", 45 | "lodash": "^4.17.21", 46 | "moment-range": "^4.0.2", 47 | "moment-timezone": "^0.5.45", 48 | "mongoose": "^8.3.2", 49 | "mongoose-id": "^0.1.3", 50 | 51 | "shelljs": "^0.8.5", 52 | "tle.js": "^4.9.0", 53 | "tough-cookie": "^4.1.3" 54 | }, 55 | "devDependencies": { 56 | "eslint": "^8.57.0", 57 | "eslint-config-airbnb-base": "^15.0.0", 58 | "eslint-plugin-import": "^2.29.1", 59 | "eslint-plugin-jest": "^28.2.0", 60 | "eslint-plugin-mongodb": "^1.0.0", 61 | "eslint-plugin-no-secrets": "^0.9.1", 62 | "eslint-plugin-security": "^3.0.0", 63 | "jest": "^29.7.0", 64 | "pino-pretty": "^11.0.0" 65 | }, 66 | "jest": { 67 | "testEnvironment": "node" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /scripts/healthcheck.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const got = require('got'); 4 | 5 | const { HEALTH_URL } = process.env; 6 | 7 | (async () => { 8 | try { 9 | await got(HEALTH_URL); 10 | process.exit(0); 11 | } catch (error) { 12 | console.log(error.message); 13 | process.exit(1); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const mongoose = require('mongoose'); 3 | const { logger } = require('./middleware/logger'); 4 | const app = require('./app'); 5 | 6 | const PORT = process.env.PORT || 6673; 7 | const SERVER = http.createServer(app.callback()); 8 | 9 | // Gracefully close Mongo connection 10 | const gracefulShutdown = () => { 11 | mongoose.connection.close(false, () => { 12 | logger.info('Mongo closed'); 13 | SERVER.close(() => { 14 | logger.info('Shutting down...'); 15 | }); 16 | }); 17 | }; 18 | 19 | // Server start 20 | SERVER.listen(PORT, '0.0.0.0', () => { 21 | logger.info(`Running on port: ${PORT}`); 22 | 23 | // Handle kill commands 24 | process.on('SIGTERM', gracefulShutdown); 25 | 26 | // Prevent dirty exit on code-fault crashes: 27 | process.on('uncaughtException', gracefulShutdown); 28 | }); 29 | -------------------------------------------------------------------------------- /services/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Export versions 3 | */ 4 | module.exports.v4 = require('./v4'); 5 | -------------------------------------------------------------------------------- /services/v4/admin/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const { auth, authz, cache } = require('../../../middleware'); 3 | 4 | const router = new Router({ 5 | prefix: '/admin', 6 | }); 7 | 8 | // Clear redis cache 9 | router.delete('/cache', auth, authz, async (ctx) => { 10 | try { 11 | await cache.redis.flushall(); 12 | ctx.status = 200; 13 | } catch (error) { 14 | ctx.throw(400, error.message); 15 | } 16 | }); 17 | 18 | // Healthcheck 19 | router.get('/health', async (ctx) => { 20 | ctx.status = 200; 21 | }); 22 | 23 | module.exports = router; 24 | -------------------------------------------------------------------------------- /services/v4/capsules/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const capsuleSchema = new mongoose.Schema({ 6 | serial: { 7 | type: String, 8 | required: true, 9 | unique: true, 10 | }, 11 | status: { 12 | type: String, 13 | enum: ['unknown', 'active', 'retired', 'destroyed'], 14 | required: true, 15 | }, 16 | dragon: { 17 | type: mongoose.ObjectId, 18 | ref: 'Dragon', 19 | }, 20 | reuse_count: { 21 | type: Number, 22 | default: 0, 23 | }, 24 | water_landings: { 25 | type: Number, 26 | default: 0, 27 | }, 28 | land_landings: { 29 | type: Number, 30 | default: 0, 31 | }, 32 | last_update: { 33 | type: String, 34 | default: null, 35 | }, 36 | launches: [{ 37 | type: mongoose.ObjectId, 38 | ref: 'Launch', 39 | }], 40 | }, { autoCreate: true }); 41 | 42 | capsuleSchema.plugin(mongoosePaginate); 43 | capsuleSchema.plugin(idPlugin); 44 | 45 | const Capsule = mongoose.model('Capsule', capsuleSchema); 46 | 47 | module.exports = Capsule; 48 | -------------------------------------------------------------------------------- /services/v4/capsules/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Capsule = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/capsules', 7 | }); 8 | 9 | // Get all capsules 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Capsule.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one capsule 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Capsule.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query capsules 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Capsule.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create capsule 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const capsule = new Capsule(ctx.request.body); 46 | await capsule.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update capsule 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Capsule.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete capsule 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Capsule.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/company/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const idPlugin = require('mongoose-id'); 3 | 4 | const companySchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | }, 8 | founder: { 9 | type: String, 10 | }, 11 | founded: { 12 | type: Number, 13 | }, 14 | employees: { 15 | type: Number, 16 | }, 17 | vehicles: { 18 | type: Number, 19 | }, 20 | launch_sites: { 21 | type: Number, 22 | }, 23 | test_sites: { 24 | type: Number, 25 | }, 26 | ceo: { 27 | type: String, 28 | }, 29 | cto: { 30 | type: String, 31 | }, 32 | coo: { 33 | type: String, 34 | }, 35 | cto_propulsion: { 36 | type: String, 37 | }, 38 | valuation: { 39 | type: Number, 40 | }, 41 | headquarters: { 42 | address: { 43 | type: String, 44 | }, 45 | city: { 46 | type: String, 47 | }, 48 | state: { 49 | type: String, 50 | }, 51 | }, 52 | links: { 53 | website: { 54 | type: String, 55 | }, 56 | flickr: { 57 | type: String, 58 | }, 59 | twitter: { 60 | type: String, 61 | }, 62 | elon_twitter: { 63 | type: String, 64 | }, 65 | }, 66 | summary: { 67 | type: String, 68 | }, 69 | }, { autoCreate: true }); 70 | 71 | companySchema.plugin(idPlugin); 72 | 73 | const Company = mongoose.model('Company', companySchema); 74 | 75 | module.exports = Company; 76 | -------------------------------------------------------------------------------- /services/v4/company/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Company = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/company', 7 | }); 8 | 9 | // Get company info 10 | router.get('/', cache(86400), async (ctx) => { 11 | try { 12 | const result = await Company.findOne({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Update company info 21 | router.patch('/:id', auth, authz, async (ctx) => { 22 | try { 23 | await Company.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 24 | ctx.status = 200; 25 | } catch (error) { 26 | ctx.throw(400, error.message); 27 | } 28 | }); 29 | 30 | module.exports = router; 31 | -------------------------------------------------------------------------------- /services/v4/cores/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const coreSchema = new mongoose.Schema({ 6 | serial: { 7 | type: String, 8 | unique: true, 9 | required: true, 10 | }, 11 | block: { 12 | type: Number, 13 | default: null, 14 | }, 15 | status: { 16 | type: String, 17 | enum: ['active', 'inactive', 'unknown', 'expended', 'lost', 'retired'], 18 | required: true, 19 | }, 20 | reuse_count: { 21 | type: Number, 22 | default: 0, 23 | }, 24 | rtls_attempts: { 25 | type: Number, 26 | default: 0, 27 | }, 28 | rtls_landings: { 29 | type: Number, 30 | default: 0, 31 | }, 32 | asds_attempts: { 33 | type: Number, 34 | default: 0, 35 | }, 36 | asds_landings: { 37 | type: Number, 38 | default: 0, 39 | }, 40 | last_update: { 41 | type: String, 42 | default: null, 43 | }, 44 | launches: [{ 45 | type: mongoose.ObjectId, 46 | ref: 'Launch', 47 | }], 48 | }, { autoCreate: true }); 49 | 50 | coreSchema.plugin(mongoosePaginate); 51 | coreSchema.plugin(idPlugin); 52 | 53 | const Core = mongoose.model('Core', coreSchema); 54 | 55 | module.exports = Core; 56 | -------------------------------------------------------------------------------- /services/v4/cores/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Core = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/cores', 7 | }); 8 | 9 | // Get all cores 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Core.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one core 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Core.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query cores 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Core.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create core 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const core = new Core(ctx.request.body); 46 | await core.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update core 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Core.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete core 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Core.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/crew/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const crewSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | default: null, 9 | }, 10 | status: { 11 | type: String, 12 | required: true, 13 | enum: ['active', 'inactive', 'retired', 'unknown'], 14 | }, 15 | agency: { 16 | type: String, 17 | default: null, 18 | }, 19 | image: { 20 | type: String, 21 | default: null, 22 | }, 23 | wikipedia: { 24 | type: String, 25 | default: null, 26 | }, 27 | launches: [{ 28 | type: mongoose.ObjectId, 29 | ref: 'Launch', 30 | }], 31 | }, { autoCreate: true }); 32 | 33 | crewSchema.plugin(mongoosePaginate); 34 | crewSchema.plugin(idPlugin); 35 | 36 | const Crew = mongoose.model('Crew', crewSchema); 37 | 38 | module.exports = Crew; 39 | -------------------------------------------------------------------------------- /services/v4/crew/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Crew = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/crew', 7 | }); 8 | 9 | // Get all crew 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Crew.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one crew member 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Crew.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query crew members 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Crew.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create crew member 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const crew = new Crew(ctx.request.body); 46 | await crew.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update crew member 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Crew.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete crew member 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Crew.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/dragons/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const dragonSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | unique: true, 9 | required: true, 10 | }, 11 | type: { 12 | type: String, 13 | required: true, 14 | }, 15 | active: { 16 | type: Boolean, 17 | required: true, 18 | }, 19 | crew_capacity: { 20 | type: Number, 21 | required: true, 22 | }, 23 | sidewall_angle_deg: { 24 | type: Number, 25 | required: true, 26 | }, 27 | orbit_duration_yr: { 28 | type: Number, 29 | required: true, 30 | }, 31 | dry_mass_kg: { 32 | type: Number, 33 | required: true, 34 | }, 35 | dry_mass_lb: { 36 | type: Number, 37 | required: true, 38 | }, 39 | first_flight: { 40 | type: String, 41 | default: null, 42 | }, 43 | heat_shield: { 44 | material: { 45 | type: String, 46 | required: true, 47 | }, 48 | size_meters: { 49 | type: Number, 50 | required: true, 51 | }, 52 | temp_degrees: { 53 | type: Number, 54 | }, 55 | dev_partner: { 56 | type: String, 57 | }, 58 | }, 59 | thrusters: { 60 | type: mongoose.Mixed, 61 | }, 62 | launch_payload_mass: { 63 | kg: { 64 | type: Number, 65 | }, 66 | lb: { 67 | type: Number, 68 | }, 69 | }, 70 | launch_payload_vol: { 71 | cubic_meters: { 72 | type: Number, 73 | }, 74 | cubic_feet: { 75 | type: Number, 76 | }, 77 | }, 78 | return_payload_mass: { 79 | kg: { 80 | type: Number, 81 | }, 82 | lb: { 83 | type: Number, 84 | }, 85 | }, 86 | return_payload_vol: { 87 | cubic_meters: { 88 | type: Number, 89 | }, 90 | cubic_feet: { 91 | type: Number, 92 | }, 93 | }, 94 | pressurized_capsule: { 95 | payload_volume: { 96 | cubic_meters: { 97 | type: Number, 98 | }, 99 | cubic_feet: { 100 | type: Number, 101 | }, 102 | }, 103 | }, 104 | trunk: { 105 | trunk_volume: { 106 | cubic_meters: { 107 | type: Number, 108 | }, 109 | cubic_feet: { 110 | type: Number, 111 | }, 112 | }, 113 | cargo: { 114 | solar_array: { 115 | type: Number, 116 | }, 117 | unpressurized_cargo: { 118 | type: Boolean, 119 | }, 120 | }, 121 | }, 122 | height_w_trunk: { 123 | meters: { 124 | type: Number, 125 | }, 126 | feet: { 127 | type: Number, 128 | }, 129 | }, 130 | diameter: { 131 | meters: { 132 | type: Number, 133 | }, 134 | feet: { 135 | type: Number, 136 | }, 137 | }, 138 | flickr_images: { 139 | type: [ 140 | String, 141 | ], 142 | }, 143 | wikipedia: { 144 | type: String, 145 | }, 146 | description: { 147 | type: String, 148 | }, 149 | }, { autoCreate: true }); 150 | 151 | dragonSchema.plugin(mongoosePaginate); 152 | dragonSchema.plugin(idPlugin); 153 | 154 | const Dragon = mongoose.model('Dragon', dragonSchema); 155 | 156 | module.exports = Dragon; 157 | -------------------------------------------------------------------------------- /services/v4/dragons/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Dragon = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/dragons', 7 | }); 8 | 9 | // Get all dragons 10 | router.get('/', cache(86400), async (ctx) => { 11 | try { 12 | const result = await Dragon.find({}, null, { sort: { name: 'asc' } }); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one dragon 21 | router.get('/:id', cache(86400), async (ctx) => { 22 | const result = await Dragon.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query dragons 31 | router.post('/query', cache(86400), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Dragon.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a dragon 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const dragon = new Dragon(ctx.request.body); 46 | await dragon.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a dragon 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Dragon.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete dragon 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Dragon.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const admin = require('./admin/routes'); 3 | const capsules = require('./capsules/routes'); 4 | const cores = require('./cores/routes'); 5 | const crew = require('./crew/routes'); 6 | const dragons = require('./dragons/routes'); 7 | const landpads = require('./landpads/routes'); 8 | const launches = require('./launches/routes'); 9 | const launchpads = require('./launchpads/routes'); 10 | const payloads = require('./payloads/routes'); 11 | const rockets = require('./rockets/routes'); 12 | const ships = require('./ships/routes'); 13 | const users = require('./users/routes'); 14 | const company = require('./company/routes'); 15 | const roadster = require('./roadster/routes'); 16 | const starlink = require('./starlink/routes'); 17 | 18 | const v4 = new Router({ 19 | prefix: '/v4', 20 | }); 21 | 22 | v4.use(admin.routes()); 23 | v4.use(capsules.routes()); 24 | v4.use(cores.routes()); 25 | v4.use(crew.routes()); 26 | v4.use(dragons.routes()); 27 | v4.use(landpads.routes()); 28 | v4.use(launches.routes()); 29 | v4.use(launchpads.routes()); 30 | v4.use(payloads.routes()); 31 | v4.use(rockets.routes()); 32 | v4.use(ships.routes()); 33 | v4.use(users.routes()); 34 | v4.use(company.routes()); 35 | v4.use(roadster.routes()); 36 | v4.use(starlink.routes()); 37 | 38 | module.exports = v4; 39 | -------------------------------------------------------------------------------- /services/v4/landpads/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const landpadSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | default: null, 9 | }, 10 | full_name: { 11 | type: String, 12 | default: null, 13 | }, 14 | status: { 15 | type: String, 16 | enum: ['active', 'inactive', 'unknown', 'retired', 'lost', 'under construction'], 17 | required: true, 18 | }, 19 | type: { 20 | type: String, 21 | default: null, 22 | }, 23 | locality: { 24 | type: String, 25 | default: null, 26 | }, 27 | region: { 28 | type: String, 29 | default: null, 30 | }, 31 | latitude: { 32 | type: Number, 33 | default: null, 34 | }, 35 | longitude: { 36 | type: Number, 37 | default: null, 38 | }, 39 | landing_attempts: { 40 | type: Number, 41 | default: 0, 42 | }, 43 | landing_successes: { 44 | type: Number, 45 | default: 0, 46 | }, 47 | wikipedia: { 48 | type: String, 49 | default: null, 50 | }, 51 | details: { 52 | type: String, 53 | default: null, 54 | }, 55 | launches: [{ 56 | type: mongoose.ObjectId, 57 | ref: 'Launch', 58 | }], 59 | }, { autoCreate: true }); 60 | 61 | landpadSchema.plugin(mongoosePaginate); 62 | landpadSchema.plugin(idPlugin); 63 | 64 | const Landpad = mongoose.model('Landpad', landpadSchema); 65 | 66 | module.exports = Landpad; 67 | -------------------------------------------------------------------------------- /services/v4/landpads/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Landpad = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/landpads', 7 | }); 8 | 9 | // Get all landpads 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Landpad.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one landpad 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Landpad.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query landpads 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Landpad.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a landpad 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const landpad = new Landpad(ctx.request.body); 46 | await landpad.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a landpad 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Landpad.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete landpad 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Landpad.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/launches/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Launch = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/launches', 7 | }); 8 | 9 | // 10 | // Convenience Endpoints 11 | // 12 | 13 | // Get past launches 14 | router.get('/past', cache(20), async (ctx) => { 15 | try { 16 | const result = await Launch.find({ 17 | upcoming: false, 18 | }, null, { 19 | sort: { 20 | flight_number: 'asc', 21 | }, 22 | }); 23 | ctx.status = 200; 24 | ctx.body = result; 25 | } catch (error) { 26 | ctx.throw(400, error.message); 27 | } 28 | }); 29 | 30 | // Get upcoming launches 31 | router.get('/upcoming', cache(20), async (ctx) => { 32 | try { 33 | const result = await Launch.find({ 34 | upcoming: true, 35 | }, null, { 36 | sort: { 37 | flight_number: 'asc', 38 | }, 39 | }); 40 | ctx.status = 200; 41 | ctx.body = result; 42 | } catch (error) { 43 | ctx.throw(400, error.message); 44 | } 45 | }); 46 | 47 | // Get latest launch 48 | router.get('/latest', cache(20), async (ctx) => { 49 | try { 50 | const result = await Launch.findOne({ 51 | upcoming: false, 52 | }, null, { 53 | sort: { 54 | flight_number: 'desc', 55 | }, 56 | }); 57 | ctx.status = 200; 58 | ctx.body = result; 59 | } catch (error) { 60 | ctx.throw(400, error.message); 61 | } 62 | }); 63 | 64 | // Get next launch 65 | router.get('/next', cache(20), async (ctx) => { 66 | try { 67 | const result = await Launch.findOne({ 68 | upcoming: true, 69 | }, null, { 70 | sort: { 71 | flight_number: 'asc', 72 | }, 73 | }); 74 | ctx.status = 200; 75 | ctx.body = result; 76 | } catch (error) { 77 | ctx.throw(400, error.message); 78 | } 79 | }); 80 | 81 | // 82 | // Standard Endpoints 83 | // 84 | 85 | // Get all launches 86 | router.get('/', cache(20), async (ctx) => { 87 | try { 88 | const result = await Launch.find({}); 89 | ctx.status = 200; 90 | ctx.body = result; 91 | } catch (error) { 92 | ctx.throw(400, error.message); 93 | } 94 | }); 95 | 96 | // Get one launch 97 | router.get('/:id', cache(20), async (ctx) => { 98 | const result = await Launch.findById(ctx.params.id); 99 | if (!result) { 100 | ctx.throw(404); 101 | } 102 | ctx.status = 200; 103 | ctx.body = result; 104 | }); 105 | 106 | // Query launches 107 | router.post('/query', cache(20), async (ctx) => { 108 | const { query = {}, options = {} } = ctx.request.body; 109 | try { 110 | const result = await Launch.paginate(query, options); 111 | ctx.status = 200; 112 | ctx.body = result; 113 | } catch (error) { 114 | ctx.throw(400, error.message); 115 | } 116 | }); 117 | 118 | // Create a launch 119 | router.post('/', auth, authz, async (ctx) => { 120 | try { 121 | const launch = new Launch(ctx.request.body); 122 | await launch.save(); 123 | ctx.status = 201; 124 | } catch (error) { 125 | ctx.throw(400, error.message); 126 | } 127 | }); 128 | 129 | // Update a launch 130 | router.patch('/:id', auth, authz, async (ctx) => { 131 | try { 132 | await Launch.findByIdAndUpdate(ctx.params.id, ctx.request.body, { 133 | runValidators: true, 134 | }); 135 | ctx.status = 200; 136 | } catch (error) { 137 | ctx.throw(400, error.message); 138 | } 139 | }); 140 | 141 | // Delete a launch 142 | router.delete('/:id', auth, authz, async (ctx) => { 143 | try { 144 | await Launch.findByIdAndDelete(ctx.params.id); 145 | ctx.status = 200; 146 | } catch (error) { 147 | ctx.throw(400, error.message); 148 | } 149 | }); 150 | 151 | module.exports = router; 152 | -------------------------------------------------------------------------------- /services/v4/launchpads/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const launchpadSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | default: null, 9 | }, 10 | full_name: { 11 | type: String, 12 | default: null, 13 | }, 14 | status: { 15 | type: String, 16 | enum: ['active', 'inactive', 'unknown', 'retired', 'lost', 'under construction'], 17 | required: true, 18 | }, 19 | locality: { 20 | type: String, 21 | default: null, 22 | }, 23 | region: { 24 | type: String, 25 | default: null, 26 | }, 27 | timezone: { 28 | type: String, 29 | default: null, 30 | }, 31 | latitude: { 32 | type: Number, 33 | default: null, 34 | }, 35 | longitude: { 36 | type: Number, 37 | default: null, 38 | }, 39 | launch_attempts: { 40 | type: Number, 41 | default: 0, 42 | }, 43 | launch_successes: { 44 | type: Number, 45 | default: 0, 46 | }, 47 | rockets: [{ 48 | type: mongoose.ObjectId, 49 | ref: 'Rocket', 50 | }], 51 | launches: [{ 52 | type: mongoose.ObjectId, 53 | ref: 'Launch', 54 | }], 55 | }, { autoCreate: true }); 56 | 57 | launchpadSchema.plugin(mongoosePaginate); 58 | launchpadSchema.plugin(idPlugin); 59 | 60 | const Launchpad = mongoose.model('Launchpad', launchpadSchema); 61 | 62 | module.exports = Launchpad; 63 | -------------------------------------------------------------------------------- /services/v4/launchpads/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Launchpad = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/launchpads', 7 | }); 8 | 9 | // Get all launchpads 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Launchpad.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one launchpad 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Launchpad.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query launchpads 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Launchpad.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a launchpad 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const launchpad = new Launchpad(ctx.request.body); 46 | await launchpad.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a launchpad 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Launchpad.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete a launchpad 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Launchpad.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/payloads/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const payloadSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | default: null, 9 | unique: true, 10 | }, 11 | type: { 12 | type: String, 13 | default: null, 14 | }, 15 | reused: { 16 | type: Boolean, 17 | default: false, 18 | }, 19 | launch: { 20 | type: mongoose.ObjectId, 21 | ref: 'Launch', 22 | default: null, 23 | }, 24 | customers: [String], 25 | norad_ids: [Number], 26 | nationalities: [String], 27 | manufacturers: [String], 28 | mass_kg: { 29 | type: Number, 30 | default: null, 31 | }, 32 | mass_lbs: { 33 | type: Number, 34 | default: null, 35 | }, 36 | orbit: { 37 | type: String, 38 | default: null, 39 | }, 40 | reference_system: { 41 | type: String, 42 | default: null, 43 | }, 44 | regime: { 45 | type: String, 46 | default: null, 47 | }, 48 | longitude: { 49 | type: Number, 50 | default: null, 51 | }, 52 | semi_major_axis_km: { 53 | type: Number, 54 | default: null, 55 | }, 56 | eccentricity: { 57 | type: Number, 58 | default: null, 59 | }, 60 | periapsis_km: { 61 | type: Number, 62 | default: null, 63 | }, 64 | apoapsis_km: { 65 | type: Number, 66 | default: null, 67 | }, 68 | inclination_deg: { 69 | type: Number, 70 | default: null, 71 | }, 72 | period_min: { 73 | type: Number, 74 | default: null, 75 | }, 76 | lifespan_years: { 77 | type: Number, 78 | default: null, 79 | }, 80 | epoch: { 81 | type: String, 82 | default: null, 83 | }, 84 | mean_motion: { 85 | type: Number, 86 | default: null, 87 | }, 88 | raan: { 89 | type: Number, 90 | default: null, 91 | }, 92 | arg_of_pericenter: { 93 | type: Number, 94 | default: null, 95 | }, 96 | mean_anomaly: { 97 | type: Number, 98 | default: null, 99 | }, 100 | dragon: { 101 | capsule: { 102 | type: mongoose.ObjectId, 103 | ref: 'Capsule', 104 | default: null, 105 | }, 106 | mass_returned_kg: { 107 | type: Number, 108 | default: null, 109 | }, 110 | mass_returned_lbs: { 111 | type: Number, 112 | default: null, 113 | }, 114 | flight_time_sec: { 115 | type: Number, 116 | default: null, 117 | }, 118 | manifest: { 119 | type: String, 120 | default: null, 121 | }, 122 | water_landing: { 123 | type: Boolean, 124 | default: null, 125 | }, 126 | land_landing: { 127 | type: Boolean, 128 | default: null, 129 | }, 130 | }, 131 | }, { autoCreate: true }); 132 | 133 | const index = { 134 | name: 'text', 135 | }; 136 | payloadSchema.index(index); 137 | 138 | payloadSchema.plugin(mongoosePaginate); 139 | payloadSchema.plugin(idPlugin); 140 | 141 | const Payload = mongoose.model('Payload', payloadSchema); 142 | 143 | module.exports = Payload; 144 | -------------------------------------------------------------------------------- /services/v4/payloads/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Payload = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/payloads', 7 | }); 8 | 9 | // Get all payloads 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Payload.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one payload 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Payload.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query payloads 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Payload.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a payload 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const payload = new Payload(ctx.request.body); 46 | await payload.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a payload 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Payload.findByIdAndUpdate(ctx.params.id, ctx.request.body, { 57 | runValidators: true, 58 | }); 59 | ctx.status = 200; 60 | } catch (error) { 61 | ctx.throw(400, error.message); 62 | } 63 | }); 64 | 65 | // Delete a payload 66 | router.delete('/:id', auth, authz, async (ctx) => { 67 | try { 68 | await Payload.findByIdAndDelete(ctx.params.id); 69 | ctx.status = 200; 70 | } catch (error) { 71 | ctx.throw(400, error.message); 72 | } 73 | }); 74 | 75 | module.exports = router; 76 | -------------------------------------------------------------------------------- /services/v4/roadster/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const idPlugin = require('mongoose-id'); 3 | 4 | const roadsterSchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | }, 8 | launch_date_utc: { 9 | type: String, 10 | }, 11 | launch_date_unix: { 12 | type: Number, 13 | }, 14 | launch_mass_kg: { 15 | type: Number, 16 | }, 17 | launch_mass_lbs: { 18 | type: Number, 19 | }, 20 | norad_id: { 21 | type: Number, 22 | }, 23 | epoch_jd: { 24 | type: Number, 25 | }, 26 | orbit_type: { 27 | type: String, 28 | }, 29 | apoapsis_au: { 30 | type: Number, 31 | }, 32 | periapsis_au: { 33 | type: Number, 34 | }, 35 | semi_major_axis_au: { 36 | type: Number, 37 | }, 38 | eccentricity: { 39 | type: Number, 40 | }, 41 | inclination: { 42 | type: Number, 43 | }, 44 | longitude: { 45 | type: Number, 46 | }, 47 | periapsis_arg: { 48 | type: Number, 49 | }, 50 | period_days: { 51 | type: Number, 52 | }, 53 | speed_kph: { 54 | type: Number, 55 | }, 56 | speed_mph: { 57 | type: Number, 58 | }, 59 | earth_distance_km: { 60 | type: Number, 61 | }, 62 | earth_distance_mi: { 63 | type: Number, 64 | }, 65 | mars_distance_km: { 66 | type: Number, 67 | }, 68 | mars_distance_mi: { 69 | type: Number, 70 | }, 71 | flickr_images: [String], 72 | wikipedia: { 73 | type: String, 74 | }, 75 | video: { 76 | type: String, 77 | }, 78 | details: { 79 | type: String, 80 | }, 81 | }, { autoCreate: true }); 82 | 83 | roadsterSchema.plugin(idPlugin); 84 | 85 | const Roadster = mongoose.model('Roadster', roadsterSchema); 86 | 87 | module.exports = Roadster; 88 | -------------------------------------------------------------------------------- /services/v4/roadster/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Roadster = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/roadster', 7 | }); 8 | 9 | // Get roadster 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Roadster.findOne({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Update roadster 21 | router.patch('/:id', auth, authz, async (ctx) => { 22 | try { 23 | await Roadster.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 24 | ctx.status = 200; 25 | } catch (error) { 26 | ctx.throw(400, error.message); 27 | } 28 | }); 29 | 30 | module.exports = router; 31 | -------------------------------------------------------------------------------- /services/v4/rockets/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Rocket = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/rockets', 7 | }); 8 | 9 | // Get all rockets 10 | router.get('/', cache(86400), async (ctx) => { 11 | try { 12 | const result = await Rocket.find({}, null, { sort: { first_flight: 'asc' } }); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one rocket 21 | router.get('/:id', cache(86400), async (ctx) => { 22 | const result = await Rocket.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query rocket 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Rocket.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a rocket 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const rocket = new Rocket(ctx.request.body); 46 | await rocket.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a rocket 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Rocket.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete a rocket 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Rocket.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/ships/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const shipSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | unique: true, 9 | required: true, 10 | }, 11 | legacy_id: { 12 | type: String, 13 | default: null, 14 | }, 15 | model: { 16 | type: String, 17 | default: null, 18 | }, 19 | type: { 20 | type: String, 21 | default: null, 22 | }, 23 | roles: [ 24 | String, 25 | ], 26 | active: { 27 | type: Boolean, 28 | required: true, 29 | }, 30 | imo: { 31 | type: Number, 32 | default: null, 33 | }, 34 | mmsi: { 35 | type: Number, 36 | default: null, 37 | }, 38 | abs: { 39 | type: Number, 40 | default: null, 41 | }, 42 | class: { 43 | type: Number, 44 | default: null, 45 | }, 46 | mass_kg: { 47 | type: Number, 48 | default: null, 49 | }, 50 | mass_lbs: { 51 | type: Number, 52 | default: null, 53 | }, 54 | year_built: { 55 | type: Number, 56 | default: null, 57 | }, 58 | home_port: { 59 | type: String, 60 | default: null, 61 | }, 62 | status: { 63 | type: String, 64 | default: null, 65 | }, 66 | speed_kn: { 67 | type: Number, 68 | default: null, 69 | }, 70 | course_deg: { 71 | type: Number, 72 | default: null, 73 | }, 74 | latitude: { 75 | type: Number, 76 | default: null, 77 | }, 78 | longitude: { 79 | type: Number, 80 | default: null, 81 | }, 82 | last_ais_update: { 83 | type: String, 84 | default: null, 85 | }, 86 | link: { 87 | type: String, 88 | default: null, 89 | }, 90 | image: { 91 | type: String, 92 | default: null, 93 | }, 94 | launches: [{ 95 | type: mongoose.ObjectId, 96 | ref: 'Launch', 97 | }], 98 | }, { autoCreate: true }); 99 | 100 | shipSchema.plugin(mongoosePaginate); 101 | shipSchema.plugin(idPlugin); 102 | 103 | const Ship = mongoose.model('Ship', shipSchema); 104 | 105 | module.exports = Ship; 106 | -------------------------------------------------------------------------------- /services/v4/ships/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Ship = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/ships', 7 | }); 8 | 9 | // Get all ships 10 | router.get('/', cache(300), async (ctx) => { 11 | try { 12 | const result = await Ship.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one ship 21 | router.get('/:id', cache(300), async (ctx) => { 22 | const result = await Ship.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query ships 31 | router.post('/query', cache(300), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Ship.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a ship 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const ship = new Ship(ctx.request.body); 46 | await ship.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a ship 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await Ship.findByIdAndUpdate(ctx.params.id, ctx.request.body, { runValidators: true }); 57 | ctx.status = 200; 58 | } catch (error) { 59 | ctx.throw(400, error.message); 60 | } 61 | }); 62 | 63 | // Delete a ship 64 | router.delete('/:id', auth, authz, async (ctx) => { 65 | try { 66 | await Ship.findByIdAndDelete(ctx.params.id); 67 | ctx.status = 200; 68 | } catch (error) { 69 | ctx.throw(400, error.message); 70 | } 71 | }); 72 | 73 | module.exports = router; 74 | -------------------------------------------------------------------------------- /services/v4/starlink/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const Starlink = require('./model'); 3 | const { auth, authz, cache } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/starlink', 7 | }); 8 | 9 | // Get all Starlink satellites 10 | router.get('/', cache(3600), async (ctx) => { 11 | try { 12 | const result = await Starlink.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one Starlink satellite 21 | router.get('/:id', cache(3600), async (ctx) => { 22 | const result = await Starlink.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query Starlink satellites 31 | router.post('/query', cache(3600), async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await Starlink.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a Starlink satellite 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const ship = new Starlink(ctx.request.body); 46 | await ship.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a Starlink satellite 54 | router.patch('/:norad_id', auth, authz, async (ctx) => { 55 | try { 56 | await Starlink.findOneAndUpdate({ 'spaceTrack.NORAD_CAT_ID': ctx.params.norad_id }, ctx.request.body, { 57 | runValidators: true, 58 | setDefaultsOnInsert: true, 59 | upsert: true, 60 | }); 61 | ctx.status = 200; 62 | } catch (error) { 63 | ctx.throw(400, error.message); 64 | } 65 | }); 66 | 67 | // Delete a Starlink satellite 68 | router.delete('/:id', auth, authz, async (ctx) => { 69 | try { 70 | await Starlink.findByIdAndDelete(ctx.params.id); 71 | ctx.status = 200; 72 | } catch (error) { 73 | ctx.throw(400, error.message); 74 | } 75 | }); 76 | 77 | module.exports = router; 78 | -------------------------------------------------------------------------------- /services/v4/users/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongoosePaginate = require('mongoose-paginate-v2'); 3 | const idPlugin = require('mongoose-id'); 4 | 5 | const userSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | default: null, 9 | }, 10 | key: { 11 | type: String, 12 | required: true, 13 | }, 14 | role: { 15 | type: String, 16 | required: true, 17 | enum: ['superuser', 'user', 'create', 'update', 'delete'], 18 | }, 19 | }, { autoCreate: true }); 20 | 21 | userSchema.plugin(mongoosePaginate); 22 | userSchema.plugin(idPlugin); 23 | 24 | const User = mongoose.model('User', userSchema); 25 | 26 | module.exports = User; 27 | -------------------------------------------------------------------------------- /services/v4/users/routes.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const User = require('./model'); 3 | const { auth, authz } = require('../../../middleware'); 4 | 5 | const router = new Router({ 6 | prefix: '/users', 7 | }); 8 | 9 | // Get all users 10 | router.get('/', auth, authz, async (ctx) => { 11 | try { 12 | const result = await User.find({}); 13 | ctx.status = 200; 14 | ctx.body = result; 15 | } catch (error) { 16 | ctx.throw(400, error.message); 17 | } 18 | }); 19 | 20 | // Get one user 21 | router.get('/:id', auth, authz, async (ctx) => { 22 | const result = await User.findById(ctx.params.id); 23 | if (!result) { 24 | ctx.throw(404); 25 | } 26 | ctx.status = 200; 27 | ctx.body = result; 28 | }); 29 | 30 | // Query users 31 | router.post('/query', auth, authz, async (ctx) => { 32 | const { query = {}, options = {} } = ctx.request.body; 33 | try { 34 | const result = await User.paginate(query, options); 35 | ctx.status = 200; 36 | ctx.body = result; 37 | } catch (error) { 38 | ctx.throw(400, error.message); 39 | } 40 | }); 41 | 42 | // Create a user 43 | router.post('/', auth, authz, async (ctx) => { 44 | try { 45 | const user = new User(ctx.request.body); 46 | await user.save(); 47 | ctx.status = 201; 48 | } catch (error) { 49 | ctx.throw(400, error.message); 50 | } 51 | }); 52 | 53 | // Update a user 54 | router.patch('/:id', auth, authz, async (ctx) => { 55 | try { 56 | await User.findByIdAndUpdate(ctx.params.id, ctx.request.body, { 57 | runValidators: true, 58 | }); 59 | ctx.status = 200; 60 | } catch (error) { 61 | ctx.throw(400, error.message); 62 | } 63 | }); 64 | 65 | // Delete a user 66 | router.delete('/:id', auth, authz, async (ctx) => { 67 | try { 68 | await User.findByIdAndDelete(ctx.params.id); 69 | ctx.status = 200; 70 | } catch (error) { 71 | ctx.throw(400, error.message); 72 | } 73 | }); 74 | 75 | module.exports = router; 76 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$SPACEX_WORKER" == "true" ]; then 4 | node ./jobs/worker.js 5 | else 6 | node ./server.js 7 | fi 8 | -------------------------------------------------------------------------------- /tests/index.test.js: -------------------------------------------------------------------------------- 1 | it('should expect true to be true', () => { 2 | expect(true).toBe(true); 3 | }); 4 | --------------------------------------------------------------------------------