{"id":18763297,"url":"https://github.com/eiberham/dragonball","last_synced_at":"2025-10-04T14:24:11.183Z","repository":{"id":42317318,"uuid":"206653001","full_name":"eiberham/dragonball","owner":"eiberham","description":":dragon: Dragon ball rest api","archived":false,"fork":false,"pushed_at":"2023-10-27T15:28:11.000Z","size":1933,"stargazers_count":18,"open_issues_count":25,"forks_count":9,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-29T17:08:30.822Z","etag":null,"topics":["api-rest","docker","dragonball","jwt-token","kubernetes","minikube","node-js","redis","swagger-api"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eiberham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-09-05T20:36:31.000Z","updated_at":"2025-04-07T15:59:16.000Z","dependencies_parsed_at":"2023-10-27T16:31:12.049Z","dependency_job_id":null,"html_url":"https://github.com/eiberham/dragonball","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eiberham/dragonball","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eiberham%2Fdragonball","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eiberham%2Fdragonball/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eiberham%2Fdragonball/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eiberham%2Fdragonball/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eiberham","download_url":"https://codeload.github.com/eiberham/dragonball/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eiberham%2Fdragonball/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278323095,"owners_count":25968070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-rest","docker","dragonball","jwt-token","kubernetes","minikube","node-js","redis","swagger-api"],"created_at":"2024-11-07T18:25:38.982Z","updated_at":"2025-10-04T14:24:11.140Z","avatar_url":"https://github.com/eiberham.png","language":"JavaScript","readme":"# Dragonball\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./dragonball.jpeg\" alt=\"dragonball\" /\u003e\n\u003c/p\u003e\n\n\u003ctable border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse: collapse; border: none;\"\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/wwleak/dragonball?style=for-the-badge\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg alt=\"GitHub code size in bytes\" src=\"https://img.shields.io/github/languages/code-size/wwleak/dragonball?style=for-the-badge\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg alt=\"GitHub top language\" src=\"https://img.shields.io/github/languages/top/wwleak/dragonball?style=for-the-badge\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg alt=\"GitHub last commit\" src=\"https://img.shields.io/github/last-commit/wwleak/dragonball?style=for-the-badge\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg alt=\"GitHub stars\" src=\"https://img.shields.io/github/stars/wwleak/dragonball?style=for-the-badge\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nThis is the dragon ball rest api, an idea to provide to anyone, relevant information about the anime.\nThe application is compound of the following features:\n\n\u003col\u003e\n  \u003cli\u003eSSL support\u003c/li\u003e\n  \u003cli\u003eData caching with Redis\u003c/li\u003e\n  \u003cli\u003eClustering via pm2 package\u003c/li\u003e\n  \u003cli\u003eJWT authentication and routes protection\u003c/li\u003e\n  \u003cli\u003eFancy OAPI Front-end\u003c/li\u003e\n\u003c/ol\u003e\n\nAs a side note endpoint tests via mocha and supertest packages were added for major robustness.\n\nThis is the list of endpoints currently available:\n\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eVerb\u003c/th\u003e\u003cth\u003eResource\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\u003cth\u003eScope\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/swagger\u003c/td\u003e\u003ctd\u003eSwagger UI\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePOST\u003c/td\u003e\u003ctd\u003e/auth\u003c/td\u003e\u003ctd\u003eAuthentication resource\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/characters\u003c/td\u003e\u003ctd\u003eGet the list of characters\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/characters/:name\u003c/td\u003e\u003ctd\u003eGet a single character\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePOST\u003c/td\u003e\u003ctd\u003e/characters\u003c/td\u003e\u003ctd\u003eCreate a character\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eDELETE\u003c/td\u003e\u003ctd\u003e/characters/:id\u003c/td\u003e\u003ctd\u003eDelete a character\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePATCH\u003c/td\u003e\u003ctd\u003e/characters/:id\u003c/td\u003e\u003ctd\u003eUpdate a character\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/sagas\u003c/td\u003e\u003ctd\u003eGet the list of sagas\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/sagas/:name\u003c/td\u003e\u003ctd\u003eGet a single saga\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePOST\u003c/td\u003e\u003ctd\u003e/sagas\u003c/td\u003e\u003ctd\u003eCreate a saga\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eDELETE\u003c/td\u003e\u003ctd\u003e/sagas/:id\u003c/td\u003e\u003ctd\u003eDelete a saga\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/films\u003c/td\u003e\u003ctd\u003eGet a list of related films\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/films/:name\u003c/td\u003e\u003ctd\u003eGet info about a single film\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePOST\u003c/td\u003e\u003ctd\u003e/films\u003c/td\u003e\u003ctd\u003eCreate a film\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eDELETE\u003c/td\u003e\u003ctd\u003e/films/:id\u003c/td\u003e\u003ctd\u003eDelete a film\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/users\u003c/td\u003e\u003ctd\u003eGet a list of users\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eGET\u003c/td\u003e\u003ctd\u003e/users/:name\u003c/td\u003e\u003ctd\u003eGet info about a single user\u003c/td\u003e\u003ctd\u003ePublic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePOST\u003c/td\u003e\u003ctd\u003e/users\u003c/td\u003e\u003ctd\u003eCreate user\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eDELETE\u003c/td\u003e\u003ctd\u003e/users/:id\u003c/td\u003e\u003ctd\u003eDelete user\u003c/td\u003e\u003ctd\u003eProtected\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\nThere's a OAPI resource to test all the endpoints, you have to log yourself in in order to test those endpoints who are protected. This is how the swagger page looks like:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./swagger.png\" alt=\"swagger\" /\u003e\n\u003c/p\u003e\n\n## :rocket: How to run it ?\n\nBefore doing anything you should clone the repo:\n\n```console\nfoo@bar:~$ git clone https://github.com/eiberham/dragonball.git\n```\n\nYou should first install docker and docker compose by running:\n\n```console\nfoo@bar:~$ sudo apt-get install docker.io docker-compose\n```\nThen log into your docker hub account by typing:\n\n```console\nfoo@bar:~$ docker login --username YOUR_USERNAME --password YOUR_PASSWORD\n```\n### :warning: Important\n\nIf for any reason, you come across with the following error while trying to log in (as i did) ...\n\n```\n`Error saving credentials: error storing credentials - err: exit status 1, out: `Error calling StartServiceByName for org.freedesktop.secrets: Timeout was reached``\n```\n\n... I could solve that by installing the following packages:\n\n ```console\nfoo@bar:~$ sudo apt install gnupg2 pass\n```\n\n...Or if you face an error like this\n\n```console\nERROR: for db Cannot start service db: driver failed programming external connectivity on endpoint\n```\nJust run\n\n ```console\nfoo@bar:~$ service mongodb stop\n ```\n\nFinally run compose:\n\n```console\nfoo@bar:~$ docker-compose up\n```\n\n## :anchor: Docker hub\n\nIf you wish to push the docker image to your docker hub account simply build the image and push it e.g:\n\n```console\n foo@bar:~$ docker build -t username/dragonball:tag .\n foo@bar:~$ docker login\n foo@bar:~$ docker push username/dragonball:tag\n```\n\nTo limit resource usage when running a container :\n\n```console\nfoo@bar:~$ docker run -d --name dragonball \\\n           --publish 8080:8080\n           --memory 200m \\\n           --memory-swap 1G \\\n           --cpu-shares 1024 \\\n           username/dragonball:tag\n```\n\n## :package: Aws\nIn order to deploy it to the cloud, for example to an ec2 instance over aws:\n\nFirst make sure you downloaded the ec2 instance's key pair to a safe place in your computer.\n\nThen you should give it proper permissions by running:\n\n```console\nfoo@bar:~$ chmod 400 dragonball.pem\n```\n\nNext step is to delete the node_modules folder directory of your project:\n\n```console\nfoo@bar:~$ rm -rf node_modules\n```\n\nNext step is to copy your project files:\n\n```console\nfoo@bar:~$ scp -r -i dragonball.pem ~/dragonball ec2-user@8.32.145.3:~/\n```\n\nNext step is to connect to the EC2 instance via SSH:\n\n```console\nfoo@bar:~$ ssh -i dragonball.pem ec2-user@8.32.145.3\n```\nThen run the commands below once you're successfully connected to install docker.\n\n```console\nsudo yum update\nsudo yum install docker\nsudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-`uname -s`-`uname -m` | sudo tee /usr/local/bin/docker-compose \u003e /dev/null\nsudo chmod +x /usr/local/bin/docker-compose\nsudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose\n```\nNow start the docker service.\n\n```console\nfoo@bar:~$ sudo service docker start\n```\n\nFinally run\n\n```console\nfoo@bar:~$ sudo docker-compose up\n```\n\nDone!\n\n## :ship: Kubernetes\n\nThis is a guideline for myself for running this api in a kubernetes cluster :stuck_out_tongue:\n\nSo alternatively, if you want to run it in a kubernetes cluster in your host machine do the following\n(tested on mac os with docker desktop):\n\nIf you take a look at the source code you'll see that the application uses mongodb by means of persistent storage and redis as a cache or volatile storage, normally third party services like these live in their own pods, so we're going to start defining the most relevant one first and then we'll follow up with the rest.\n\nFrom the docs\n\n\u003e minikube quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows. We proudly focus on helping application developers and new Kubernetes users.\n\nFrom the docs\n\n\u003e Helm helps you manage Kubernetes applications — Helm Charts help you define, install, and upgrade even the most complex Kubernetes application. Charts are easy to create, version, share, and publish — so start using Helm and stop the copy-and-paste.\n\nBefore anything make sure you have installed  minikube and helm in your machine and proceed to create a kubernetes cluster:\n\n```bash\nbrew update\ncurl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64\nsudo install minikube-darwin-amd64 /usr/local/bin/minikube\nbrew install helm\nminikube start --vm=true\n```\n\n### Express\n\nFirst off we need to conteinerize the application by building a docker image, on the root folder you'll find a dockerfile. Later on, when creating the deployment objects we will need it.\n\n**dockerfile**\n\n```bash\nFROM node:10.16.0\nWORKDIR /usr/src/app\nCOPY package*.json ./\nRUN npm install --only=prod\nCOPY . .\nRUN npm install -g nodemon\nEXPOSE 3000\nCMD npm run dev\n```\n\nIn order to build the image get into the application's root directory, run this command, log into your dockerhub account and push the image\n\n```bash\ncd dragonball\ndocker build -t eiberham/dragonball:v1 .\ndocker login\ndocker push eiberham/dragonball:v1\n```\n\nOnce completed we have to define our express deployment and service objects. For the service we have to define its type as load balancer so that it can redirect traffic to the right pod based on network load.\n\n**express.yml**\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: express\nspec:\n  selector:\n    app: express\n  ports:\n    - name: \"3000\"\n      port: 3000\n      targetPort: 3000\n  type: LoadBalancer\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: express\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: express\n  template:\n    metadata:\n      labels:\n        app: express\n    spec:\n      containers:\n        - image: eiberham/dragonball:v1\n          name: dragonball\n          ports:\n            - containerPort: 3000\n          imagePullPolicy: Always\n```\n\nNow run:\n\n```bash\nkubectl apply -f express.yml\n```\n\nUltimately, as we have a service of **type:LoadBalancer** we'd need to expose the service by using ingress, but before let us enable the ingress add-on in minikube :\n\n```bash\nminikube addons enable ingress\n```\n\n**ingress.yml**\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: entrance\n  annotations:\n    nginx.ingress.kubernetes.io/rewrite-target: /$1\nspec:\n  rules:\n    - host: localhost\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: express\n                port:\n                  number: 3000\n```\n\nNow run:\n\n```bash\nkubectl apply -f ingress.yml\n```\n\n### Mongo\n\nThe mongo instance will have authentication enabled so we'll need to provide username and password. Let us create the secrets.yml file that is going to hold our credentials:\n\n**secrets.yml**\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: mongo-secret\ntype: Opaque\nstringData:\n  USERNAME: eiberham\n  PASSWORD: eiberham\n```\n\nNow run:\n```bash\nkubectl apply -f secrets.yml\n``````\n\nSince the idea is to  we create the bash script that will prepopulate the database, we're going to create a seeding bash script. For now, the most important thing to have is a user who can authenticate and issue requests to protected routes:\n\n**seeding.sh**\n\n```bash\nmongosh \"mongodb://127.0.0.1:27017/dragonball\" --username $USERNAME -p $PASSWORD --authenticationDatabase dragonball \u003c\u003c'EOF'\ndb.users.insertOne({\n    username : 'admin',\n    password : '$2b$10$al8KvO3PCchoB/nmwU6XZ.HjpmGRSw48SS8U8P0IjRuQlfkJKISUK',\n    name : 'Admin',\n    profile : 2.0\n})\ndb.characters.insertOne({\n    name: 'Goku',\n    description: 'Goku is the main protagonist of the dragon ball series',\n    avatar:\n        'https://vignette.wikia.nocookie.net/dragonball/images/5/5b/Gokusteppingoutofaspaceship.jpg/revision/latest/scale-to-width-down/224?cb=20150325220848'\n})\nEOF\n```\n\nIn the code above we have defined an insert for the users collection using **admin** as username and password as well as an insert for the characters collection.\n\n```bash\nkubectl create configmap seeding-configmap --from-file=seeding.sh\n```\n\nIn order to create the deployment/service for the mongo database we will use helm, but beforehand we have to seed the database.\n\n**values.yml**\n\n```yaml\nmongodb:\n  auth:\n    usernames:\n      - eiberham\n    passwords:\n      - eiberham\n    databases:\n      - dragonball\n  initdbScripts:\n    enabled: true\n    configMapName: seeding-configmap\n  extraEnvVars:\n  - name: USERNAME\n    valueFrom:\n      secretkeyRef:\n        name: mongo-secret\n        key: username\n  - name: PASSWORD\n    valueFrom:\n      secretkeyRef:\n        name: mongo-secret\n        key: password\n```\n\nNow it's time to install the mongo chart. If you wish to look\n\n```bash\nnoglob helm install mongo oci://registry-1.docker.io/bitnamicharts/mongodb \\\n--set auth.usernames[0]=$(kubectl get secret mongo-secret -o jsonpath='{.data.USERNAME}' | base64 --decode) \\\n--set auth.passwords[0]=$(kubectl get secret mongo-secret -o jsonpath='{.data.PASSWORD}' | base64 --decode) \\\n--set auth.databases[0]=dragonball \\\n--set initdbScriptsConfigMap=seeding-configmap \\\n--set extraEnvVarsSecret=mongo-secret \\\n--values values.yml --debug\n```\n\nIf you want to check the underlying manifest for this helm release you can issue the following command:\n\n```bash\nhelm get manifest mongo\n```\n\nYou can either check the pod logs to make sure there was no error or log into the pod and query any seeded collection. Plus the seeding script should be located at inside the pod.\n\n```bash\nkubectl logs \u003c\u003cpod\u003e\u003e\nkubectl exec it \u003c\u003cpod\u003e\u003e /bin/bash\nmongosh \"mongodb://127.0.0.1:27017/dragonball\" --username eiberham -p eiberham --authenticationDatabase dragonball\ndb.characters.find()\n```\n\n### Redis\n\nFinally we need a redis instance, so we will install a helm chart with authentication disabled and just one replica:\n\n```bash\nnoglob helm install redis oci://registry-1.docker.io/bitnamicharts/redis \\\n--set auth.enabled=false \\\n--set replica.replicaCount=1\n```\n\nIf you want to check the underlying manifest for this helm release you can issue the following command:\n\n```bash\nhelm get manifest redis\n```\n\nNow check if pods are running\n\n```bash\nkubectl get pods\n```\n\nBy having every piece of the puzzle in place proceed to issue the following command to get the express url:\n\n```bash\nminikube service express --url\n```\n\nThat should give us the express service's url and port. Now if you issue a curl to the auth endpoint it should work:\n\n```bash\ncurl -X POST https://192.168.64.30:30136/api/auth -k -d '{ \"user\":\"admin\", \"password\":\"admin\" }' -H 'Content-Type: application/json'\n```\n\n## :cloud: Google cloud\n\nFirst log in \n\n```bash\ngcloud auth login\n```\n\nNow set the project where you want to create the kubernetes cluster\n\n```bash\ngcloud config set project DRAGONBALL_PROJECT_ID\n```\n\nThen create a cluster by running this command:\n\n```bash\ngcloud container clusters create dragonball \\\n--num-nodes 2 \\\n--machine-type n1-standard-2 \\\n--zone us-central1-a\n```\n\nGet the credentials\n\n```bash\ngcloud container clusters get-credentials dragonball --zone us-central1-a\n```\n\nUpdate coming soon ...\n\n## Contributing\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n## :pushpin: License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details\n\n\u003cp align=\"right\"\u003eMADE WITH ❤ BY ABRAHAM\u003c/p\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feiberham%2Fdragonball","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feiberham%2Fdragonball","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feiberham%2Fdragonball/lists"}