├── .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 |
--------------------------------------------------------------------------------