├── .gitignore ├── Dockerfile ├── update_api.sh ├── package.json ├── .github └── workflows │ └── docker.yml ├── main_js_api_code.py ├── README.md └── generate_api.py /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coriolis 3 | coriolis-data 4 | api.log 5 | conversions.json 6 | .idea 7 | _* -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk add npm python3 git nodejs 4 | 5 | COPY . /coriolis-api 6 | WORKDIR /coriolis-api 7 | 8 | RUN ./build.sh 9 | 10 | CMD ["node", "coriolis-api.js"] 11 | -------------------------------------------------------------------------------- /update_api.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # check for running api 4 | check_screen=$(screen -ls | grep coriolis-api) 5 | 6 | if [ "$check_screen" != "" ]; then 7 | echo "Running api found, terminating.." 8 | screen -XS coriolis-api quit 9 | fi 10 | 11 | # update api 12 | . build.sh 13 | 14 | # start api 15 | screen -L -Logfile api.log -mdS coriolis-api node coriolis-api.js 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coriolis-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Gobidev/coriolis-api.git" 12 | }, 13 | "author": "Gobidev", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/Gobidev/coriolis-api/issues" 17 | }, 18 | "homepage": "https://github.com/Gobidev/coriolis-api#readme", 19 | "dependencies": { 20 | "body-parser": "^1.20.3", 21 | "express": "^4.21.2", 22 | "express-winston": "^4.1.0", 23 | "lodash": "^4.17.21", 24 | "lz-string": "^1.4.4", 25 | "winston": "^3.3.3", 26 | "zlib": "^1.0.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Create and publish Docker image 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | workflow_dispatch: 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | jobs: 13 | build-and-push-image: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Log in to the Container registry 24 | uses: docker/login-action@v3 25 | with: 26 | registry: ${{ env.REGISTRY }} 27 | username: ${{ github.actor }} 28 | password: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Extract metadata (tags, labels) for Docker 31 | id: meta 32 | uses: docker/metadata-action@v5 33 | with: 34 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 35 | 36 | - name: Build and push Docker image 37 | uses: docker/build-push-action@v6 38 | with: 39 | context: . 40 | push: true 41 | tags: ${{ steps.meta.outputs.tags }} 42 | labels: ${{ steps.meta.outputs.labels }} 43 | -------------------------------------------------------------------------------- /main_js_api_code.py: -------------------------------------------------------------------------------- 1 | 2 | api_constant_imports = """ 3 | const Dist = require('./coriolis-data/dist/index'); 4 | const LZString = require('lz-string'); 5 | const Lodash = require('lodash'); 6 | const zlib = require('zlib'); 7 | const express = require("express"); 8 | const bodyParser = require("body-parser"); 9 | const jsonParser = bodyParser.json(); 10 | const app = express(); 11 | const fs = require('fs')""" 12 | 13 | main_api_js = """ 14 | app.post("/convert", jsonParser, (req, res) => { 15 | 16 | var shipName = req.body.ShipName 17 | 18 | let ship = shipFromLoadoutJSON(req.body) 19 | ship.updateStats() 20 | 21 | let response = toDetailedBuild(shipName, ship, ship.toString()) 22 | 23 | // Get client information for log 24 | let logInformation = {"headers:": req.headers, "shipName": shipName} 25 | console.log(JSON.stringify(logInformation)) 26 | 27 | // Save converted build 28 | buildToBeAdded = response.references[0].url 29 | console.log(buildToBeAdded) 30 | 31 | // Test if file exists 32 | if (!fs.existsSync("conversions.json")) { 33 | fs.writeFile("conversions.json", "[]", err => { 34 | if (err) { 35 | console.error(err) 36 | return 37 | } 38 | }) 39 | } 40 | 41 | fs.readFile("conversions.json", function (err, data) { 42 | var json = JSON.parse(data) 43 | json.push([+new Date(), buildToBeAdded]) 44 | 45 | fs.writeFile("conversions.json", JSON.stringify(json, null, 4), err => { 46 | if (err) { 47 | console.error(err) 48 | return 49 | } 50 | }) 51 | }) 52 | 53 | res.type("json") 54 | res.send(response) 55 | }) 56 | 57 | app.listen(7777, () => { 58 | console.log("API started successfully") 59 | }) 60 | """ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # coriolis-api 2 | An API that converts [Elite: Dangerous](https://www.elitedangerous.com/) Loadout event entries to the ship json format 3 | used by [Coriolis](https://coriolis.io/). 4 | 5 | ## Scope of this project 6 | The original purpose of this project is its implementation in my project [EDNeutronAssistant](https://github.com/Gobidev/EDNeutronAssistant). 7 | As I have planned to integrate the [exact spansh plotter](https://www.spansh.co.uk/exact-plotter) API into this program, 8 | I searched for a way to convert the loadout event entries of the Elite: Dangerous 9 | log to the ship json format used by coriolis, as this is required for the spansh route calculation and has use in other parts of the 10 | program. Because all the conversion operations of Coriolis are done client-side, there was no easy way for me to interface 11 | with the conversion functions of Coriolis with my python program. Therefore, I decided to create a NodeJS based API that 12 | utilizes the existing JavaScript functions of Coriolis to allow for an easy implementation in python. To avoid having to 13 | constantly update the API when new ships or modules are added to the game, I made a python script (generate_api.py) that 14 | automatically generates the NodeJS API file from the [Coriolis GitHub](https://github.com/EDCD/coriolis) repository. 15 | 16 | ## Using the API 17 | My own instance of this API is running at ``https://coriolis-api.gobidev.de``. 18 | To convert a loadout event to a Coriolis JSON, send a POST request to ``https://coriolis-api.gobidev.de/convert`` with 19 | the loadout event as a JSON in the request body. The converted Coriolis build will be sent back as a JSON. 20 | 21 | ## Building the API 22 | If you are on Linux, or have the option to run bash on Windows (i.e. by using git bash), building the API is as easy as 23 | cloning this repository, installing the required node modules with ``npm i`` and then executing build.sh. Note that NodeJS 24 | as well as python3.9 or higher (might also work with earlier versions) have to be installed for this to work. 25 | 26 | ## Running the API 27 | After building the API it can be run by executing index.js with ``node index.js``. Per default, it listens on port 7777/tcp 28 | and requests are processed on http://localhost:7777/convert. Requests must be POST requests with a JSON body that matches 29 | an Elite: Dangerous loadout event. -------------------------------------------------------------------------------- /generate_api.py: -------------------------------------------------------------------------------- 1 | import main_js_api_code 2 | import hashlib 3 | import time 4 | 5 | main_files = [ 6 | "./coriolis/src/app/utils/CompanionApiUtils.js", 7 | "./coriolis/src/app/utils/BlueprintFunctions.js", 8 | "./coriolis/src/app/utils/UtilityFunctions.js", 9 | "./coriolis/src/app/utils/UrlGenerators.js", 10 | "./coriolis/src/app/utils/JournalUtils.js", 11 | 12 | "./coriolis/src/app/shipyard/Ship.js", 13 | "./coriolis/src/app/shipyard/Module.js", 14 | "./coriolis/src/app/shipyard/Calculations.js", 15 | "./coriolis/src/app/shipyard/ModuleUtils.js", 16 | "./coriolis/src/app/shipyard/ModuleSet.js", 17 | "./coriolis/src/app/shipyard/StatsFormatting.js", 18 | "./coriolis/src/app/shipyard/Serializer.js", 19 | "./coriolis/src/app/shipyard/Constants.js", 20 | ] 21 | 22 | replacement_dict = { 23 | "Module.": "", 24 | "Calc.": "", 25 | "ModuleUtils.": "", 26 | 27 | "export ": "", 28 | "default ": "", 29 | 30 | "Modifications.": "Dist.Modifications.", 31 | 32 | "Ships[": "Dist.Ships[", 33 | "Modules.": "Dist.Modules.", 34 | "Modules,": "Dist.Modules,", 35 | 36 | "json.Dist.Modules.": "json.Modules.", 37 | 38 | "_.": "Lodash.", 39 | "chain(": "Lodash.chain(", 40 | 41 | "this.jumpRange(": "jumpRange(", 42 | } 43 | 44 | 45 | def parse_file(filename: str) -> str: 46 | with open(filename, "r") as f: 47 | parsed_file = f.read() 48 | 49 | # Apply replacement_dict 50 | for item in replacement_dict: 51 | parsed_file = parsed_file.replace(item, replacement_dict[item]) 52 | 53 | # Split lines 54 | file_lines = parsed_file.split("\n") 55 | 56 | # Remove all imports 57 | for index, line in enumerate(file_lines): 58 | if line.startswith("import ") or line.startswith("const zlib"): 59 | file_lines[index] = "" 60 | 61 | # Remove double empty lines 62 | for index, line in enumerate(file_lines): 63 | if index < len(file_lines) - 1: 64 | if line == "" and file_lines[index + 1] == "": 65 | del file_lines[index] 66 | 67 | # Insert filename 68 | file_lines.insert(0, f"\n\n//------------------------------------------------\n//{filename}") 69 | 70 | # Recombine lines 71 | parsed_file = "\n".join(file_lines) 72 | 73 | # Remove redundant methods 74 | file_methods = parsed_file.split("/**") 75 | indicator_strings = ["", "div>", ""] 76 | 77 | for index, method in enumerate(file_methods): 78 | for indicator in indicator_strings: 79 | if indicator in method: 80 | del file_methods[index] 81 | 82 | parsed_file = "/**".join(file_methods) 83 | 84 | return parsed_file 85 | 86 | 87 | def generate_api_file() -> str: 88 | output_string = main_js_api_code.api_constant_imports 89 | 90 | for file in main_files: 91 | output_string += parse_file(file) 92 | 93 | output_string += main_js_api_code.main_api_js 94 | 95 | return output_string 96 | 97 | 98 | if __name__ == '__main__': 99 | api_file_string = generate_api_file() 100 | 101 | api_string_sha256 = hashlib.sha256(api_file_string.encode("utf-8")).hexdigest() 102 | timestamp = time.strftime("%Y-%m-%d %T") 103 | 104 | info_top = f"""\n/** 105 | This file was automatically generated by generate_api.py of https://github.com/Gobidev/coriolis-api 106 | 107 | Time of generation: {timestamp} 108 | Build Hash: {api_string_sha256}\n**/\n 109 | """ 110 | 111 | with open("coriolis-api.js", "w") as api_file: 112 | api_file.write(info_top + api_file_string) 113 | print("API built successfully!") 114 | --------------------------------------------------------------------------------