main 4254ef2be844 cached
67 files
105.7 KB
25.0k tokens
81 symbols
1 requests
Download .txt
Repository: Benjamin-Code-YouTube/boumboum-back
Branch: main
Commit: 4254ef2be844
Files: 67
Total size: 105.7 KB

Directory structure:
gitextract_bylbss8l/

├── .adonisrc.json
├── .dockerignore
├── .editorconfig
├── .github/
│   └── workflows/
│       └── build.yml
├── .gitignore
├── Dockerfile
├── README.md
├── ace
├── ace-manifest.json
├── app/
│   ├── Controllers/
│   │   └── Http/
│   │       ├── GendersController.ts
│   │       ├── MatchesController.ts
│   │       ├── ProfilesController.ts
│   │       ├── SpotifyController.ts
│   │       └── UsersController.ts
│   ├── Exceptions/
│   │   └── Handler.ts
│   ├── Middleware/
│   │   ├── Auth.ts
│   │   └── SilentAuth.ts
│   ├── Models/
│   │   ├── ApiToken.ts
│   │   ├── Artist.ts
│   │   ├── Gender.ts
│   │   ├── Genre.ts
│   │   ├── Match.ts
│   │   ├── Profile.ts
│   │   ├── SocialToken.ts
│   │   ├── Track.ts
│   │   └── User.ts
│   ├── Services/
│   │   └── SpotifyService.ts
│   └── Validators/
│       ├── CreateMatchValidator.ts
│       └── CreateProfileValidator.ts
├── commands/
│   └── index.ts
├── compose.yml
├── config/
│   ├── ally.ts
│   ├── app.ts
│   ├── auth.ts
│   ├── bodyparser.ts
│   ├── cors.ts
│   ├── database.ts
│   ├── drive.ts
│   └── hash.ts
├── contracts/
│   ├── ally.ts
│   ├── auth.ts
│   ├── drive.ts
│   ├── env.ts
│   ├── events.ts
│   ├── hash.ts
│   └── tests.ts
├── database/
│   ├── factories/
│   │   └── index.ts
│   ├── migrations/
│   │   ├── 1698432448829_users.ts
│   │   ├── 1698432448832_api_tokens.ts
│   │   ├── 1698440823891_social_tokens.ts
│   │   ├── 1698491899562_genders.ts
│   │   ├── 1698491899563_profiles.ts
│   │   ├── 1698528332152_artists.ts
│   │   ├── 1698570713652_genres.ts
│   │   ├── 1698571837102_tracks.ts
│   │   └── 1698942906705_matches.ts
│   └── seeders/
│       └── Gender.ts
├── env.ts
├── package.json
├── providers/
│   └── AppProvider.ts
├── server.ts
├── start/
│   ├── kernel.ts
│   └── routes.ts
├── test.ts
├── tests/
│   ├── bootstrap.ts
│   └── functional/
│       └── hello_world.spec.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .adonisrc.json
================================================
{
  "typescript": true,
  "commands": [
    "./commands",
    "@adonisjs/core/build/commands/index.js",
    "@adonisjs/repl/build/commands",
    "@adonisjs/lucid/build/commands"
  ],
  "exceptionHandlerNamespace": "App/Exceptions/Handler",
  "aliases": {
    "App": "app",
    "Config": "config",
    "Database": "database",
    "Contracts": "contracts"
  },
  "preloads": [
    "./start/routes",
    "./start/kernel"
  ],
  "providers": [
    "./providers/AppProvider",
    "@adonisjs/core",
    "@adonisjs/lucid",
    "@adonisjs/ally",
    "@adonisjs/auth"
  ],
  "aceProviders": [
    "@adonisjs/repl"
  ],
  "tests": {
    "suites": [
      {
        "name": "functional",
        "files": [
          "tests/functional/**/*.spec(.ts|.js)"
        ],
        "timeout": 60000
      }
    ]
  },
  "testProviders": [
    "@japa/preset-adonis/TestsProvider"
  ]
}

================================================
FILE: .dockerignore
================================================
node_modules


================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.json]
insert_final_newline = false

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .github/workflows/build.yml
================================================
name: Build

on:
  push:
    branches:
      - main

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v1

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to GitHub Packages
        uses: docker/login-action@v1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Prepare metadata
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: ghcr.io/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=ref,event=tag

      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          build-args: |
            APP_RELEASE=${{ github.sha }}
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Deploy the new image
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.KEY }}
          script: |
            cd ~/stacks
            docker stack deploy -c boumboum-back.yml boumboum-back --with-registry-auth


================================================
FILE: .gitignore
================================================
node_modules
build
coverage
.vscode
.DS_STORE
.env
tmp


================================================
FILE: Dockerfile
================================================
FROM node:20-alpine3.18 as base

RUN apk --no-cache add curl

# All deps stage
FROM base as deps
WORKDIR /app
ADD package.json package-lock.json ./
RUN npm ci

# Production only deps stage
FROM base as production-deps
WORKDIR /app
ADD package.json package-lock.json ./
RUN npm ci --omit=dev
RUN wget https://gobinaries.com/tj/node-prune --output-document - | /bin/sh && node-prune

# Build stage
FROM base as build
WORKDIR /app
COPY --from=deps /app/node_modules /app/node_modules
ADD . .
RUN node ace build --production --ignore-ts-errors

# Production stage
FROM base
ENV NODE_ENV=production
WORKDIR /app
COPY --from=production-deps /app/node_modules /app/node_modules
COPY --from=build /app/build /app
EXPOSE 8080
CMD ["node", "./server.js"]


================================================
FILE: README.md
================================================
# Music Match API with AdonisJS

An innovative application that connects people based on their musical preferences, drawing inspiration from Tinder.

## User Flow

### User Registration/Login
- Users sign up or log in using their Spotify credentials.

### Profile Creation
- Users complete their profiles, including basic information (name, date of birth, brief description, gender preference, and profile picture).
- Users select and customize their top 4 favorite tracks from Spotify, retrieved using the Spotify API.

### Matchmaking
- Users are presented with potential matches based on their gender preference.

### Interaction
- Users see their matches.
- For mutual matches, they can view the email of the match.

## API Requirements

We seek a skilled Node.js developer to create this API using the AdonisJS framework. The chosen database engine can be PostgreSQL or MySQL based on your expertise and recommendation.

## Required Endpoints

1. **User Registration/Login**
   - Implement OAuth2 authentication with Spotify to allow users to register or log in.

2. **Logout Endpoint**
   - Provide a secure endpoint for users to log out of their accounts.

3. **Profile Management**
   - Create endpoints for users to complete their profiles, including basic information and their preferred Spotify tracks.

4. **Matching Endpoint**
   - Develop an endpoint to retrieve and match users based on their gender preference.

5. **Spotify Integration**
   - Implement an endpoint to search for songs using the Spotify API.

6. **Match History**
   - Develop an endpoint to list all matches, indicating whether the match is mutual. Include relevant user data for each match.

## Additional Spotify Data

Upon user registration, it's crucial to save the user's Spotify preferences for future use, including:
- User's top 20 artists
- User's top 20 tracks
- Artists the user follows

## Technical Details

- **Framework:** AdonisJS
- **Database:** MySQL
- **Authentication:** OAuth2 with Spotify

## Getting Started

1. **Install Dependencies**
    ```bash
    npm install
    ```

2. **Configure Database Connection**
    - Set up the database connection details in your `.env` file.

3. **Configure Spotify API**
    - Add your Spotify client_id and client_secret in your `.env` file.

4. **Run Migrations and Seed Data**
    ```bash
    node ace migration:run
    node ace db:seed
    ```

5. **Start the Application**
    ```bash
    npm run dev
    ```

6. **Access the API at**
   - [http://localhost:3333/api](http://localhost:3333/api)

## API Documentation

- For the Login API (http://localhost:3333/api/signin), open it in a browser as the request will be redirected to Spotify for authentication. This API won't work in Postman.
- All other APIs are provided in the Postman collection.


================================================
FILE: ace
================================================
/*
|--------------------------------------------------------------------------
| Ace Commands
|--------------------------------------------------------------------------
|
| This file is the entry point for running ace commands.
|
*/

require('reflect-metadata')
require('source-map-support').install({ handleUncaughtExceptions: false })

const { Ignitor } = require('@adonisjs/core/build/standalone')
new Ignitor(__dirname)
  .ace()
  .handle(process.argv.slice(2))


================================================
FILE: ace-manifest.json
================================================
{
  "commands": {
    "dump:rcfile": {
      "settings": {},
      "commandPath": "@adonisjs/core/build/commands/DumpRc",
      "commandName": "dump:rcfile",
      "description": "Dump contents of .adonisrc.json file along with defaults",
      "args": [],
      "aliases": [],
      "flags": []
    },
    "list:routes": {
      "settings": {
        "loadApp": true,
        "stayAlive": true
      },
      "commandPath": "@adonisjs/core/build/commands/ListRoutes/index",
      "commandName": "list:routes",
      "description": "List application routes",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "verbose",
          "propertyName": "verbose",
          "type": "boolean",
          "description": "Display more information"
        },
        {
          "name": "reverse",
          "propertyName": "reverse",
          "type": "boolean",
          "alias": "r",
          "description": "Reverse routes display"
        },
        {
          "name": "methods",
          "propertyName": "methodsFilter",
          "type": "array",
          "alias": "m",
          "description": "Filter routes by method"
        },
        {
          "name": "patterns",
          "propertyName": "patternsFilter",
          "type": "array",
          "alias": "p",
          "description": "Filter routes by the route pattern"
        },
        {
          "name": "names",
          "propertyName": "namesFilter",
          "type": "array",
          "alias": "n",
          "description": "Filter routes by route name"
        },
        {
          "name": "json",
          "propertyName": "json",
          "type": "boolean",
          "description": "Output as JSON"
        },
        {
          "name": "table",
          "propertyName": "table",
          "type": "boolean",
          "description": "Output as Table"
        },
        {
          "name": "max-width",
          "propertyName": "maxWidth",
          "type": "number",
          "description": "Specify maximum rendering width. Ignored for JSON Output"
        }
      ]
    },
    "generate:key": {
      "settings": {},
      "commandPath": "@adonisjs/core/build/commands/GenerateKey",
      "commandName": "generate:key",
      "description": "Generate a new APP_KEY secret",
      "args": [],
      "aliases": [],
      "flags": []
    },
    "repl": {
      "settings": {
        "loadApp": true,
        "environment": "repl",
        "stayAlive": true
      },
      "commandPath": "@adonisjs/repl/build/commands/AdonisRepl",
      "commandName": "repl",
      "description": "Start a new REPL session",
      "args": [],
      "aliases": [],
      "flags": []
    },
    "db:seed": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/DbSeed",
      "commandName": "db:seed",
      "description": "Execute database seeders",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection for the seeders",
          "alias": "c"
        },
        {
          "name": "interactive",
          "propertyName": "interactive",
          "type": "boolean",
          "description": "Run seeders in interactive mode",
          "alias": "i"
        },
        {
          "name": "files",
          "propertyName": "files",
          "type": "array",
          "description": "Define a custom set of seeders files names to run",
          "alias": "f"
        },
        {
          "name": "compact-output",
          "propertyName": "compactOutput",
          "type": "boolean",
          "description": "A compact single-line output"
        }
      ]
    },
    "db:wipe": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/DbWipe",
      "commandName": "db:wipe",
      "description": "Drop all tables, views and types in database",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "drop-views",
          "propertyName": "dropViews",
          "type": "boolean",
          "description": "Drop all views"
        },
        {
          "name": "drop-types",
          "propertyName": "dropTypes",
          "type": "boolean",
          "description": "Drop all custom types (Postgres only)"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explicitly force command to run in production"
        }
      ]
    },
    "db:truncate": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/DbTruncate",
      "commandName": "db:truncate",
      "description": "Truncate all tables in database",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explicitly force command to run in production"
        }
      ]
    },
    "make:model": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/MakeModel",
      "commandName": "make:model",
      "description": "Make a new Lucid model",
      "args": [
        {
          "type": "string",
          "propertyName": "name",
          "name": "name",
          "required": true,
          "description": "Name of the model class"
        }
      ],
      "aliases": [],
      "flags": [
        {
          "name": "migration",
          "propertyName": "migration",
          "type": "boolean",
          "alias": "m",
          "description": "Generate the migration for the model"
        },
        {
          "name": "controller",
          "propertyName": "controller",
          "type": "boolean",
          "alias": "c",
          "description": "Generate the controller for the model"
        },
        {
          "name": "factory",
          "propertyName": "factory",
          "type": "boolean",
          "alias": "f",
          "description": "Generate a factory for the model"
        }
      ]
    },
    "make:migration": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/MakeMigration",
      "commandName": "make:migration",
      "description": "Make a new migration file",
      "args": [
        {
          "type": "string",
          "propertyName": "name",
          "name": "name",
          "required": true,
          "description": "Name of the migration file"
        }
      ],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "The connection flag is used to lookup the directory for the migration file"
        },
        {
          "name": "folder",
          "propertyName": "folder",
          "type": "string",
          "description": "Pre-select a migration directory"
        },
        {
          "name": "create",
          "propertyName": "create",
          "type": "string",
          "description": "Define the table name for creating a new table"
        },
        {
          "name": "table",
          "propertyName": "table",
          "type": "string",
          "description": "Define the table name for altering an existing table"
        }
      ]
    },
    "make:seeder": {
      "settings": {},
      "commandPath": "@adonisjs/lucid/build/commands/MakeSeeder",
      "commandName": "make:seeder",
      "description": "Make a new Seeder file",
      "args": [
        {
          "type": "string",
          "propertyName": "name",
          "name": "name",
          "required": true,
          "description": "Name of the seeder class"
        }
      ],
      "aliases": [],
      "flags": []
    },
    "make:factory": {
      "settings": {},
      "commandPath": "@adonisjs/lucid/build/commands/MakeFactory",
      "commandName": "make:factory",
      "description": "Make a new factory",
      "args": [
        {
          "type": "string",
          "propertyName": "model",
          "name": "model",
          "required": true,
          "description": "The name of the model"
        }
      ],
      "aliases": [],
      "flags": [
        {
          "name": "model-path",
          "propertyName": "modelPath",
          "type": "string",
          "description": "The path to the model"
        },
        {
          "name": "exact",
          "propertyName": "exact",
          "type": "boolean",
          "description": "Create the factory with the exact name as provided",
          "alias": "e"
        }
      ]
    },
    "migration:run": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/Migration/Run",
      "commandName": "migration:run",
      "description": "Migrate database by running pending migrations",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explicitly force to run migrations in production"
        },
        {
          "name": "dry-run",
          "propertyName": "dryRun",
          "type": "boolean",
          "description": "Do not run actual queries. Instead view the SQL output"
        },
        {
          "name": "compact-output",
          "propertyName": "compactOutput",
          "type": "boolean",
          "description": "A compact single-line output"
        },
        {
          "name": "disable-locks",
          "propertyName": "disableLocks",
          "type": "boolean",
          "description": "Disable locks acquired to run migrations safely"
        }
      ]
    },
    "migration:rollback": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/Migration/Rollback",
      "commandName": "migration:rollback",
      "description": "Rollback migrations to a specific batch number",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explictly force to run migrations in production"
        },
        {
          "name": "dry-run",
          "propertyName": "dryRun",
          "type": "boolean",
          "description": "Do not run actual queries. Instead view the SQL output"
        },
        {
          "name": "batch",
          "propertyName": "batch",
          "type": "number",
          "description": "Define custom batch number for rollback. Use 0 to rollback to initial state"
        },
        {
          "name": "compact-output",
          "propertyName": "compactOutput",
          "type": "boolean",
          "description": "A compact single-line output"
        },
        {
          "name": "disable-locks",
          "propertyName": "disableLocks",
          "type": "boolean",
          "description": "Disable locks acquired to run migrations safely"
        }
      ]
    },
    "migration:status": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/Migration/Status",
      "commandName": "migration:status",
      "description": "View migrations status",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        }
      ]
    },
    "migration:reset": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/Migration/Reset",
      "commandName": "migration:reset",
      "description": "Rollback all migrations",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explicitly force command to run in production"
        },
        {
          "name": "dry-run",
          "propertyName": "dryRun",
          "type": "boolean",
          "description": "Do not run actual queries. Instead view the SQL output"
        },
        {
          "name": "disable-locks",
          "propertyName": "disableLocks",
          "type": "boolean",
          "description": "Disable locks acquired to run migrations safely"
        }
      ]
    },
    "migration:refresh": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/Migration/Refresh",
      "commandName": "migration:refresh",
      "description": "Rollback and migrate database",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explicitly force command to run in production"
        },
        {
          "name": "dry-run",
          "propertyName": "dryRun",
          "type": "boolean",
          "description": "Do not run actual queries. Instead view the SQL output"
        },
        {
          "name": "seed",
          "propertyName": "seed",
          "type": "boolean",
          "description": "Run seeders"
        },
        {
          "name": "disable-locks",
          "propertyName": "disableLocks",
          "type": "boolean",
          "description": "Disable locks acquired to run migrations safely"
        }
      ]
    },
    "migration:fresh": {
      "settings": {
        "loadApp": true
      },
      "commandPath": "@adonisjs/lucid/build/commands/Migration/Fresh",
      "commandName": "migration:fresh",
      "description": "Drop all tables and re-migrate the database",
      "args": [],
      "aliases": [],
      "flags": [
        {
          "name": "connection",
          "propertyName": "connection",
          "type": "string",
          "description": "Define a custom database connection",
          "alias": "c"
        },
        {
          "name": "force",
          "propertyName": "force",
          "type": "boolean",
          "description": "Explicitly force command to run in production"
        },
        {
          "name": "seed",
          "propertyName": "seed",
          "type": "boolean",
          "description": "Run seeders"
        },
        {
          "name": "drop-views",
          "propertyName": "dropViews",
          "type": "boolean",
          "description": "Drop all views"
        },
        {
          "name": "drop-types",
          "propertyName": "dropTypes",
          "type": "boolean",
          "description": "Drop all custom types (Postgres only)"
        },
        {
          "name": "disable-locks",
          "propertyName": "disableLocks",
          "type": "boolean",
          "description": "Disable locks acquired to run migrations safely"
        }
      ]
    }
  },
  "aliases": {}
}


================================================
FILE: app/Controllers/Http/GendersController.ts
================================================
import type { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";

import Gender from "App/Models/Gender";

export default class GendersController {
  public async index({ response }: HttpContextContract) {
    try {
      const genders = await Gender.query();
      return response.json({
        status: true,
        data: genders,
        message: "Successfully fetched genders",
      });
    } catch (err) {
      return response.json({
        status: false,
        message: "Something went wrong.",
      });
    }
  }
}


================================================
FILE: app/Controllers/Http/MatchesController.ts
================================================
import type { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";
import { schema } from '@ioc:Adonis/Core/Validator'

import User from "App/Models/User";
import Match from "App/Models/Match";
import CreateMatchValidator from "App/Validators/CreateMatchValidator";

export default class MatchesController {
  //retrive list of user's based on there gender preference
  public async get({ response, auth }: HttpContextContract) {
    try {
      const userId = auth.user?.id;
      if (!userId) {
        return response.json({
          message: "kindly login",
        });
      }

      const currentUser = await User.query()
        .where("id", userId)
        .preload("profile")
        .first();
      const profile = currentUser?.profile;

      if (!profile?.preferedGenderId) return response.json("Profile not exist");

      const users = await User.query()
        .whereNot("id", userId)
        .preload("profile")
        .with("profile", (q) => {
          q.where("prefered_gender_id", profile.preferedGenderId);
        });

      const mappedUsers = users?.map((u) => {
        return {
          id: u.id,
          name: u.name,
          avatar: u?.profile?.avatar,
        };
      });
      return response.json({
        data: mappedUsers,
      });
    } catch (err) {
      console.log("errrrr", err);
    }
  }

  public async mutualMatch({ request, response, auth }: HttpContextContract) {
    try {
      const authId = auth.user?.id;
      if (!authId) {
        response.status(401);
        return;
      }

      const payload = await request.validate(CreateMatchValidator);
      const { userId } = payload;
      if(authId == userId) {
        return response.json({
          message: "Cannot mark youself as match."
        })
      }

      const userExist = await User.query().where("id", userId).first();
      if(!userExist) {
        return response.json({
          message: "User not found."
        })
      }

      const matchExist = await Match.query()
        .where("matcher_user_id", userId)
        .where("matched_user_id", authId)
        .first();

      if (matchExist) {
        await Match.query().where("id", matchExist.id).update({
          mutual_match: 1,
          match_date: new Date(),
        });

        const matchedUser = await User.query()
          .where("id", userId)
          .select("name", "email")
          // .preload("profile")
          .first();

        const userData = {
          name: matchedUser?.name,
          email: matchedUser?.email,
        };

        return response.json({
          message: "It's a mutual match",
          data: userData,
        });
      }

      const newMatch = new Match();
      newMatch.matcherUserId = authId;
      newMatch.matchedUserId = userId;
      await newMatch.save();

      return response.json({
        message: "Match has been marked.",
      });
    } catch (err) {
      return response.json({
        message: "Something went wrong.",
        errors: err?.messages?.errors,
      });
    }
  }
  public async history({ response, auth }: HttpContextContract) {
    try {
      const authId = auth.user?.id;
      if (!authId) {
        return response.json({
          message: "kindly login",
        });
      }

      const matchHistory = await Match.query()
        .where("matcher_user_id", authId)
        .orWhere("matched_user_id", authId)
        .where("mutual_match", 1);

      const userIds = matchHistory?.map((history: Match) => {
        return history.matcherUserId == authId
          ? String(history.matchedUserId)
          : String(history.matcherUserId);
      });

      const users = await User.query()
        .whereIn("id", userIds)
        .select("name", "email")

      return response.json({
        data: users,
        message: "Mutual match history.",
      });
    } catch (err) {
      return response.json({
        message: "Something went wrong.",
      });
    }
  }
}


================================================
FILE: app/Controllers/Http/ProfilesController.ts
================================================
import type { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";

import Profile from "App/Models/Profile";
import SpotifyService from "App/Services/SpotifyService";
import CreateProfileValidator from "App/Validators/CreateProfileValidator";

export default class ProfilesController {
  public async get({ response, auth }: HttpContextContract) {
    try {
      const userId = auth.user?.id;
      if (!userId) {
        response.status(401);
        return;
      }

      const profile = await Profile.query().where("user_id", userId).first();
      return response.json({
        data: profile,
      });
    } catch (err) {
      return response.json({
        status: false,
        message: "Something went wrong.",
      });
    }
  }
  public async store({ request, response, auth }: HttpContextContract) {
    try {
      const userId = auth.user?.id;
      if (!userId) {
        response.status(401);
        return;
      }
      const payload = await request.validate(CreateProfileValidator);
      const { dateOfBirth, description, preferedGenderId, trackIds } =
      payload;

      const fileName = `${new Date().getTime()}.${payload.avatar.subtype}`;
      await payload.avatar.moveToDisk("./", {
        name: fileName,
      });

      //save top 4 selected tracks by user
      const favoriteTracks = await SpotifyService.updateFavorityTrack(
        userId,
        trackIds
      );
      if (!favoriteTracks?.status)
        throw Error("unable to update favorite tracks");

      //Profile saved
      let profile;
      profile = await Profile.query().where("user_id", userId).first();
      profile = profile ? profile : new Profile();
      profile.dateOfBirth = new Date(dateOfBirth);
      profile.description = description;
      profile.avatar = `/uploads/${fileName}`;
      profile.preferedGenderId = preferedGenderId;
      profile.userId = userId;
      await profile.save();

      return response.json({
        status: true,
        message: "Profile Successfully Created",
        data: profile,
      });
    } catch (err) {
      return response.json({
        status: false,
        message: "Unable to create profile",
        errors: err?.messages?.errors,
      });
    }
  }
}


================================================
FILE: app/Controllers/Http/SpotifyController.ts
================================================
import type { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";
import SpotifyService from "App/Services/SpotifyService";

export default class SpotifiesController {
  public async artists({ response, auth }: HttpContextContract) {
    try {
      const userId = auth.user?.id;
      const topArtists = await SpotifyService.getArtists(userId);
      const savedArtists = await SpotifyService.saveArtists(userId, topArtists);
      return response.json(savedArtists);
    } catch (err) {
      console.log("err", err);
    }
  }

  public async tracks({ response, auth }: HttpContextContract) {
    try {
      const userId = auth.user?.id;
      const topTracks = await SpotifyService.getTracks(userId);
      const mappdTracks = topTracks?.map((track) => {
        return {
          // image: track?.preview_url,
          // uri: track.uri,
          popularity: track.popularity,
          name: track.name,
          trackId: track.id,
          album: track?.albun?.name,
          // artists: artists,
        };
      });
      // const savedTracks = await SpotifyService.saveTracks(userId, topTracks);

      return response.json(mappdTracks);
    } catch (err) {
      console.log("err", err);
    }
  }

  public async trackByName({request, response, auth}: HttpContextContract) {
    try {
      const userId = auth.user?.id;
      const { name } = request.qs()

      const tracks = await SpotifyService.getTracksByName(userId, name)
      
      const mappedTracks = tracks?.map((track) => {
        return  {
          uri: track.uri,
          popularity: track.popularity,
          name: track.name,
          trackId: track.id,
          album: track?.album?.name,
        }
      }) 

      return response.json({
        status: true,
        data: mappedTracks
      })
    } catch(err) {
      return response.json({
        status: false,
        message: "Error fetching track's"
      })
    }
  }
  
}


================================================
FILE: app/Controllers/Http/UsersController.ts
================================================
import type { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";
import Artist from "App/Models/Artist";
import SocialToken from "App/Models/SocialToken";
import Track from "App/Models/Track";
import User from "App/Models/User";
import SpotifyService from "App/Services/SpotifyService";

export default class UsersController {
  public async redirect({ ally }: HttpContextContract) {
    return ally.use("spotify").stateless().redirect();
  }

  public async handleCallback({ ally, auth, response }: HttpContextContract) {
    try {
      const spotify = ally.use("spotify").stateless();

      /**
       * User has explicitly denied the login request
       */
      if (spotify.accessDenied()) {
        return "Access was denied";
      }

      /**
       * Unable to verify the CSRF state
       */
      if (spotify.stateMisMatch()) {
        return "Request expired. try again";
      }

      /**
       * There was an unknown error during the redirect
       */
      if (spotify.hasError()) {
        return spotify.getError();
      }

      /**
       * Managing error states here
       */

      const user = await spotify.user();

      const { token } = user;

      const findUser = {
        email: user.email as string,
      };

      const userDetails = {
        name: user.name as string,
        email: user.email as string,
        provider: "spotify",
        access_token: token.token as any,
      };

      const newUser = await User.firstOrCreate(findUser, userDetails);

      if (!newUser) {
        return response.json({
          status: false,
          message: "Something went wrong.",
        });
      }

      /* Save Social Token */
      let socialToken = await SocialToken.query()
        .where("user_id", newUser.id)
        .first();

      socialToken = socialToken ? socialToken : new SocialToken();
      socialToken.user_id = newUser.id;
      socialToken.token = token.token;
      socialToken.refreshToken = token.refreshToken;
      socialToken.type = token.type;
      socialToken.expiresAt = token.expiresAt?.toString();

      await socialToken.save();
      /* Save Social Token */

      //save top 20 tracks from spotify service
      const trackExist = await Track.query().where("user_id", newUser.id);
      if (!trackExist?.length) {
        const topTracks = await SpotifyService.getTracks(newUser.id);
        const topTracksSaved = await SpotifyService.saveTracks(
          newUser.id,
          topTracks
        );
        if (!topTracksSaved?.status) throw Error("Unable to save top tracks");
      }

      //save top 20 artists from spotify service

      const artists = await Artist.query().where("user_id", newUser.id);
      if (!artists?.length) {
        const topArtists = await SpotifyService.getArtists(newUser.id);
        const topArtistsSaved = await SpotifyService.saveArtists(
          newUser.id,
          topArtists
        );
        if (!topArtistsSaved?.status)
          throw Error("Unable to save artists tracks");
      }

      // Generate API token

      const userToken = await auth.use("api").generate(newUser, {
        expiresIn: "90 mins",
      });

      response.json({ /* newUser, */ userToken /* , socialToken */ });
    } catch (err) {
      response.json({
        status: false,
        message: "Something went wrong.",
      });
    }
  }

  public async logout({ auth, response }: HttpContextContract) {
    await auth.use("api").revoke();
    return response.json({
      revoked: true,
    });
  }
}


================================================
FILE: app/Exceptions/Handler.ts
================================================
/*
|--------------------------------------------------------------------------
| Http Exception Handler
|--------------------------------------------------------------------------
|
| AdonisJs will forward all exceptions occurred during an HTTP request to
| the following class. You can learn more about exception handling by
| reading docs.
|
| The exception handler extends a base `HttpExceptionHandler` which is not
| mandatory, however it can do lot of heavy lifting to handle the errors
| properly.
|
*/

import Logger from '@ioc:Adonis/Core/Logger'
import HttpExceptionHandler from '@ioc:Adonis/Core/HttpExceptionHandler'

export default class ExceptionHandler extends HttpExceptionHandler {
  constructor () {
    super(Logger)
  }
}


================================================
FILE: app/Middleware/Auth.ts
================================================
import { AuthenticationException } from '@adonisjs/auth/build/standalone'
import type { GuardsList } from '@ioc:Adonis/Addons/Auth'
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

/**
 * Auth middleware is meant to restrict un-authenticated access to a given route
 * or a group of routes.
 *
 * You must register this middleware inside `start/kernel.ts` file under the list
 * of named middleware.
 */
export default class AuthMiddleware {
  /**
   * The URL to redirect to when request is Unauthorized
   */
  protected redirectTo = '/login'

  /**
   * Authenticates the current HTTP request against a custom set of defined
   * guards.
   *
   * The authentication loop stops as soon as the user is authenticated using any
   * of the mentioned guards and that guard will be used by the rest of the code
   * during the current request.
   */
  protected async authenticate(auth: HttpContextContract['auth'], guards: (keyof GuardsList)[]) {
    /**
     * Hold reference to the guard last attempted within the for loop. We pass
     * the reference of the guard to the "AuthenticationException", so that
     * it can decide the correct response behavior based upon the guard
     * driver
     */
    let guardLastAttempted: string | undefined

    for (let guard of guards) {
      guardLastAttempted = guard

      if (await auth.use(guard).check()) {
        /**
         * Instruct auth to use the given guard as the default guard for
         * the rest of the request, since the user authenticated
         * succeeded here
         */
        auth.defaultGuard = guard
        return true
      }
    }

    /**
     * Unable to authenticate using any guard
     */
    throw new AuthenticationException(
      'Unauthorized access',
      'E_UNAUTHORIZED_ACCESS',
      guardLastAttempted,
      this.redirectTo,
    )
  }

  /**
   * Handle request
   */
  public async handle (
    { auth }: HttpContextContract,
    next: () => Promise<void>,
    customGuards: (keyof GuardsList)[]
  ) {
    /**
     * Uses the user defined guards or the default guard mentioned in
     * the config file
     */
    const guards = customGuards.length ? customGuards : [auth.name]
    await this.authenticate(auth, guards)
    await next()
  }
}


================================================
FILE: app/Middleware/SilentAuth.ts
================================================
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

/**
 * Silent auth middleware can be used as a global middleware to silent check
 * if the user is logged-in or not.
 *
 * The request continues as usual, even when the user is not logged-in.
 */
export default class SilentAuthMiddleware {
  /**
   * Handle request
   */
  public async handle({ auth }: HttpContextContract, next: () => Promise<void>) {
    /**
     * Check if user is logged-in or not. If yes, then `ctx.auth.user` will be
     * set to the instance of the currently logged in user.
     */
    await auth.check()
    await next()
  }
}


================================================
FILE: app/Models/ApiToken.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class ApiToken extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public token: String
}


================================================
FILE: app/Models/Artist.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, HasMany, column, hasMany } from '@ioc:Adonis/Lucid/Orm'
import Genre from './Genre'

export default class Artist extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public userId: String

  @column()
  public name: String

  @column()
  public type: String
  
  @column()
  public popularity: String
  
  @column()
  public uri: String
  
  @column()
  public spotifyArtistId: String
  
  @column()
  public artistImage: String


  @hasMany(() => Genre)
  public genres: HasMany<typeof Genre>

}


================================================
FILE: app/Models/Gender.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class Gender extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public name: string

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}


================================================
FILE: app/Models/Genre.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class Genre extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public name: String

  @column()
  public artistId: Number
}


================================================
FILE: app/Models/Match.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class Match extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public matcherUserId: Number

  @column()
  public matchedUserId: Number

  @column()
  public mutualMatch: Boolean

  @column()
  public matchDate: Date
}


================================================
FILE: app/Models/Profile.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, HasOne, column, hasOne } from '@ioc:Adonis/Lucid/Orm'
import Gender from './Gender'

export default class Profile extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public dateOfBirth: Date
  
  @column()
  public description: String
  
  @column()
  public avatar: String

  @column()
  public preferedGenderId: Number

  @column()
  public userId: Number

  @hasOne(() => Gender)
  public preferedGender: HasOne<typeof Gender>

}


================================================
FILE: app/Models/SocialToken.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class SocialToken extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public user_id: number

  @column()
  public token: string

  @column()
  public type: string

  @column()
  public refreshToken: string

  @column()
  public expiresAt: String

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}


================================================
FILE: app/Models/Track.ts
================================================
import { DateTime } from 'luxon'
import { BaseModel, HasMany, column, hasMany } from '@ioc:Adonis/Lucid/Orm'
import Artist from './Artist'

export default class Track extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public userId: String

  @column()
  public uri: String
  
  @column()
  public popularity: String

  @column()
  public name: String

  
  @column()
  public tractImage: String
  
  @column()
  public trackId: String
  
  @column()
  public album: String


  @column()
  public favorite: Number


  @hasMany(() => Artist)
  public artists: HasMany<typeof Artist>


}


================================================
FILE: app/Models/User.ts
================================================
import { DateTime } from 'luxon'
import Hash from '@ioc:Adonis/Core/Hash'
import { column, beforeSave, BaseModel, hasMany, HasMany, hasOne, HasOne } from '@ioc:Adonis/Lucid/Orm'
import Track from './Track'
import Artist from './Artist'
import Profile from './Profile'

export default class User extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public name: string

  @column()
  public provider: string

  @column()
  public access_token: string

  @column()
  public email: string

  @column({ serializeAs: null })
  public password: string

  @column()
  public rememberMeToken: string | null

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  // @beforeSave()
  // public static async hashPassword (user: User) {
  //   if (user.$dirty.password) {
  //     user.password = await Hash.make(user.password)
  //   }
  // }

  @hasOne(() => Profile)
  public profile: HasOne<typeof Profile>

  @hasMany(() => Track)
  public tracks: HasMany<typeof Track>

  @hasMany(() => Artist)
  public artists: HasMany<typeof Artist>

}


================================================
FILE: app/Services/SpotifyService.ts
================================================
import Env from "@ioc:Adonis/Core/Env";

import Artist from "App/Models/Artist";
import Genre from "App/Models/Genre";
import SocialToken from "App/Models/SocialToken";
import Track from "App/Models/Track";
import Axios from "axios";

export default class SpotifyService {
  public static async getArtists(userId) {
    const SPOTIFY_URL = Env.get("SPOTIFY_URL");
    const social = await SocialToken.query().where("user_id", userId).first();

    const resp = await Axios.get(`${SPOTIFY_URL}/me/top/artists`, {
      headers: {
        Authorization: `Bearer ${social?.token}`,
      },
    });

    return resp?.data?.items;
  }

  public static async getTracks(userId) {
    const SPOTIFY_URL = Env.get("SPOTIFY_URL");
    const social = await SocialToken.query().where("user_id", userId).first();
    const resp = await Axios.get(
      `${SPOTIFY_URL}/me/top/tracks?time_range=medium_term&limit=5`,
      {
        headers: {
          Authorization: `Bearer ${social?.token}`,
        },
      }
    );
    return resp?.data?.items;
  }

  public static async saveArtists(userId, artists) {
    try {
      for (let artist of artists) {
        //store artists
        const newArtist = new Artist();
        newArtist.userId = userId;
        newArtist.name = artist?.name;
        newArtist.type = artist?.type;
        newArtist.popularity = artist?.popularity;
        newArtist.uri = artist?.uri;
        newArtist.spotifyArtistId = artist?.id;
        newArtist.artistImage = artist?.images ? artist?.images[0]?.url : null;

        artist.genres = artist.genres?.map((name) => {
          let genre = new Genre();
          genre.name = name;
          return genre;
        });

        //store artists genres
        await newArtist.related("genres").saveMany(artist.genres);
      }
      return {
        status: true,
      };
    } catch (err) {
      console.log("FAILIURE");
      console.log("err", err);
      return {
        status: false,
      };
    }
  }

  public static async saveTracks(userId, tracks) {
    try {
      for (let track of tracks) {
        //store tracks
        const newTrack = new Track();
        newTrack.userId = userId;
        newTrack.uri = track.uri;
        newTrack.popularity = track.popularity;
        newTrack.name = track.name;
        newTrack.trackId = track.id;
        newTrack.album = track?.album?.name;
        await newTrack.save();
      }
      return {
        status: true,
      };
    } catch (err) {
      return {
        status: false,
      };
    }
  }

  public static async getTracksByIds(userId, trackIds) {
    const SPOTIFY_URL = Env.get("SPOTIFY_URL");

    const commaSeparatedIds = trackIds.join(",");
    const social = await SocialToken.query().where("user_id", userId).first();
    const resp = await Axios.get(
      `${SPOTIFY_URL}/tracks?ids=${commaSeparatedIds}`,
      {
        headers: {
          Authorization: `Bearer ${social?.token}`,
        },
      }
    );
    return resp?.data?.tracks;
  }

  public static getTracksData(tracks) {
    const mappdTracks = tracks?.map((track) => {
      return {
        popularity: track.popularity,
        name: track.name,
        trackId: track.id,
        album: track?.albun?.name,
      };
    });

    return mappdTracks;
  }

  public static async updateFavorityTrack(userId, trackIds) {
    try {
      const markFavorite = await Track.query()
        .where("user_id", userId)
        .whereIn("track_id", trackIds)
        .update({
          favorite: 1,
        });

      return {
        status: true,
        data: markFavorite,
      };
    } catch (err) {
      return {
        status: false,
      };
    }
  }

  public static async getTracksByName(userId, name) {
    const SPOTIFY_URL = Env.get("SPOTIFY_URL");
    const social = await SocialToken.query().where("user_id", userId).first();
    const resp = await Axios.get(
      `${SPOTIFY_URL}/search?q=track:${name}&type=track`,
      {
        headers: {
          Authorization: `Bearer ${social?.token}`,
        },
      }
    );
    return resp?.data?.tracks?.items;
  }
}


================================================
FILE: app/Validators/CreateMatchValidator.ts
================================================
import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class CreateMatchValidator {
  constructor(protected ctx: HttpContextContract) {}

  /*
   * Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
   *
   * For example:
   * 1. The username must be of data type string. But then also, it should
   *    not contain special characters or numbers.
   *    ```
   *     schema.string({}, [ rules.alpha() ])
   *    ```
   *
   * 2. The email must be of data type string, formatted as a valid
   *    email. But also, not used by any other user.
   *    ```
   *     schema.string({}, [
   *       rules.email(),
   *       rules.unique({ table: 'users', column: 'email' }),
   *     ])
   *    ```
   */

  public schema = schema.create({
    userId: schema.number(),
  })

  /**
   * Custom messages for validation failures. You can make use of dot notation `(.)`
   * for targeting nested fields and array expressions `(*)` for targeting all
   * children of an array. For example:
   *
   * {
   *   'profile.username.required': 'Username is required',
   *   'scores.*.number': 'Define scores as valid numbers'
   * }
   *
   */
  public messages = {
    required: 'The {{ field }} is required.',
    // 'username.unique': 'Username not available'
  }
}


================================================
FILE: app/Validators/CreateProfileValidator.ts
================================================
import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class CreateProfileValidator {
  constructor(protected ctx: HttpContextContract) {}

  /*
   * Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
   *
   * For example:
   * 1. The username must be of data type string. But then also, it should
   *    not contain special characters or numbers.
   *    ```
   *     schema.string({}, [ rules.alpha() ])
   *    ```
   *
   * 2. The email must be of data type string, formatted as a valid
   *    email. But also, not used by any other user.
   *    ```
   *     schema.string({}, [
   *       rules.email(),
   *       rules.unique({ table: 'users', column: 'email' }),
   *     ])
   *    ```
   */

  public schema = schema.create({
    dateOfBirth: schema.string(),
    description: schema.string.nullableAndOptional(),
    preferedGenderId: schema.number(),
    trackIds: schema.array().members(schema.string()),
    avatar: schema.file({
      size: "5mb",
      extnames: ["jpg", "png", "jpeg"],
    }),
  })

  /**
   * Custom messages for validation failures. You can make use of dot notation `(.)`
   * for targeting nested fields and array expressions `(*)` for targeting all
   * children of an array. For example:
   *
   * {
   *   'profile.username.required': 'Username is required',
   *   'scores.*.number': 'Define scores as valid numbers'
   * }
   *
   */
  public messages = {
    required: 'The {{ field }} is required.',
    // 'username.unique': 'Username not available'
  }
}


================================================
FILE: commands/index.ts
================================================
import { listDirectoryFiles } from '@adonisjs/core/build/standalone'
import Application from '@ioc:Adonis/Core/Application'

/*
|--------------------------------------------------------------------------
| Exporting an array of commands
|--------------------------------------------------------------------------
|
| Instead of manually exporting each file from this directory, we use the
| helper `listDirectoryFiles` to recursively collect and export an array
| of filenames.
|
| Couple of things to note:
|
| 1. The file path must be relative from the project root and not this directory.
| 2. We must ignore this file to avoid getting into an infinite loop
|
*/
export default listDirectoryFiles(__dirname, Application.appRoot, ['./commands/index'])


================================================
FILE: compose.yml
================================================
services:
  db:
    image: mysql:8.0
    platform: linux/x86_64
    ports:
      - 3306:3306
    volumes:
      - mysql-data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_ALLOW_EMPTY_PASSWORD=true

volumes:
  mysql-data:


================================================
FILE: config/ally.ts
================================================
/**
 * Config source: https://git.io/JOdi5
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import Env from '@ioc:Adonis/Core/Env'
import { AllyConfig } from '@ioc:Adonis/Addons/Ally'

/*
|--------------------------------------------------------------------------
| Ally Config
|--------------------------------------------------------------------------
|
| The `AllyConfig` relies on the `SocialProviders` interface which is
| defined inside `contracts/ally.ts` file.
|
*/
const allyConfig: AllyConfig = {
  /*
  |--------------------------------------------------------------------------
  | Spotify driver
  |--------------------------------------------------------------------------
  */
  spotify: {
    driver: 'spotify',
    clientId: Env.get('SPOTIFY_CLIENT_ID'),
    clientSecret: Env.get('SPOTIFY_CLIENT_SECRET'),
    callbackUrl: Env.get('SPOTIFY_CALLBACK_URL', 'http://localhost:3333/api/signin-callback'),
    scopes: ['user-read-email', 'user-top-read', 'user-follow-read'],
    showDialog: false
  },
}

export default allyConfig


================================================
FILE: config/app.ts
================================================
/**
 * Config source: https://git.io/JfefZ
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import proxyAddr from 'proxy-addr'
import Env from '@ioc:Adonis/Core/Env'
import type { ServerConfig } from '@ioc:Adonis/Core/Server'
import type { LoggerConfig } from '@ioc:Adonis/Core/Logger'
import type { ProfilerConfig } from '@ioc:Adonis/Core/Profiler'
import type { ValidatorConfig } from '@ioc:Adonis/Core/Validator'

/*
|--------------------------------------------------------------------------
| Application secret key
|--------------------------------------------------------------------------
|
| The secret to encrypt and sign different values in your application.
| Make sure to keep the `APP_KEY` as an environment variable and secure.
|
| Note: Changing the application key for an existing app will make all
| the cookies invalid and also the existing encrypted data will not
| be decrypted.
|
*/
export const appKey: string = Env.get('APP_KEY')

/*
|--------------------------------------------------------------------------
| Http server configuration
|--------------------------------------------------------------------------
|
| The configuration for the HTTP(s) server. Make sure to go through all
| the config properties to make keep server secure.
|
*/
export const http: ServerConfig = {
  /*
  |--------------------------------------------------------------------------
  | Allow method spoofing
  |--------------------------------------------------------------------------
  |
  | Method spoofing enables defining custom HTTP methods using a query string
  | `_method`. This is usually required when you are making traditional
  | form requests and wants to use HTTP verbs like `PUT`, `DELETE` and
  | so on.
  |
  */
  allowMethodSpoofing: false,

  /*
  |--------------------------------------------------------------------------
  | Subdomain offset
  |--------------------------------------------------------------------------
  */
  subdomainOffset: 2,

  /*
  |--------------------------------------------------------------------------
  | Request Ids
  |--------------------------------------------------------------------------
  |
  | Setting this value to `true` will generate a unique request id for each
  | HTTP request and set it as `x-request-id` header.
  |
  */
  generateRequestId: false,

  /*
  |--------------------------------------------------------------------------
  | Trusting proxy servers
  |--------------------------------------------------------------------------
  |
  | Define the proxy servers that AdonisJs must trust for reading `X-Forwarded`
  | headers.
  |
  */
  trustProxy: proxyAddr.compile('loopback'),

  /*
  |--------------------------------------------------------------------------
  | Generating Etag
  |--------------------------------------------------------------------------
  |
  | Whether or not to generate an etag for every response.
  |
  */
  etag: false,

  /*
  |--------------------------------------------------------------------------
  | JSONP Callback
  |--------------------------------------------------------------------------
  */
  jsonpCallbackName: 'callback',

  /*
  |--------------------------------------------------------------------------
  | Cookie settings
  |--------------------------------------------------------------------------
  */
  cookie: {
    domain: '',
    path: '/',
    maxAge: '2h',
    httpOnly: true,
    secure: false,
    sameSite: false,
  },

  /*
  |--------------------------------------------------------------------------
  | Force Content Negotiation
  |--------------------------------------------------------------------------
  |
  | The internals of the framework relies on the content negotiation to
  | detect the best possible response type for a given HTTP request.
  |
  | However, it is a very common these days that API servers always wants to
  | make response in JSON regardless of the existence of the `Accept` header.
  |
  | By setting `forceContentNegotiationTo = 'application/json'`, you negotiate
  | with the server in advance to always return JSON without relying on the
  | client to set the header explicitly.
  |
  */
  forceContentNegotiationTo: 'application/json',
}

/*
|--------------------------------------------------------------------------
| Logger
|--------------------------------------------------------------------------
*/
export const logger: LoggerConfig = {
  /*
  |--------------------------------------------------------------------------
  | Application name
  |--------------------------------------------------------------------------
  |
  | The name of the application you want to add to the log. It is recommended
  | to always have app name in every log line.
  |
  | The `APP_NAME` environment variable is automatically set by AdonisJS by
  | reading the `name` property from the `package.json` file.
  |
  */
  name: Env.get('APP_NAME'),

  /*
  |--------------------------------------------------------------------------
  | Toggle logger
  |--------------------------------------------------------------------------
  |
  | Enable or disable logger application wide
  |
  */
  enabled: true,

  /*
  |--------------------------------------------------------------------------
  | Logging level
  |--------------------------------------------------------------------------
  |
  | The level from which you want the logger to flush logs. It is recommended
  | to make use of the environment variable, so that you can define log levels
  | at deployment level and not code level.
  |
  */
  level: Env.get('LOG_LEVEL', 'info'),

  /*
  |--------------------------------------------------------------------------
  | Pretty print
  |--------------------------------------------------------------------------
  |
  | It is highly advised NOT to use `prettyPrint` in production, since it
  | can have huge impact on performance.
  |
  */
  prettyPrint: Env.get('NODE_ENV') === 'development',
}

/*
|--------------------------------------------------------------------------
| Profiler
|--------------------------------------------------------------------------
*/
export const profiler: ProfilerConfig = {
  /*
  |--------------------------------------------------------------------------
  | Toggle profiler
  |--------------------------------------------------------------------------
  |
  | Enable or disable profiler
  |
  */
  enabled: true,

  /*
  |--------------------------------------------------------------------------
  | Blacklist actions/row labels
  |--------------------------------------------------------------------------
  |
  | Define an array of actions or row labels that you want to disable from
  | getting profiled.
  |
  */
  blacklist: [],

  /*
  |--------------------------------------------------------------------------
  | Whitelist actions/row labels
  |--------------------------------------------------------------------------
  |
  | Define an array of actions or row labels that you want to whitelist for
  | the profiler. When whitelist is defined, then `blacklist` is ignored.
  |
  */
  whitelist: [],
}

/*
|--------------------------------------------------------------------------
| Validator
|--------------------------------------------------------------------------
|
| Configure the global configuration for the validator. Here's the reference
| to the default config https://git.io/JT0WE
|
*/
export const validator: ValidatorConfig = {
}


================================================
FILE: config/auth.ts
================================================
/**
 * Config source: https://git.io/JY0mp
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import type { AuthConfig } from '@ioc:Adonis/Addons/Auth'

/*
|--------------------------------------------------------------------------
| Authentication Mapping
|--------------------------------------------------------------------------
|
| List of available authentication mapping. You must first define them
| inside the `contracts/auth.ts` file before mentioning them here.
|
*/
const authConfig: AuthConfig = {
  guard: 'api',
  guards: {
    /*
    |--------------------------------------------------------------------------
    | OAT Guard
    |--------------------------------------------------------------------------
    |
    | OAT (Opaque access tokens) guard uses database backed tokens to authenticate
    | HTTP request. This guard DOES NOT rely on sessions or cookies and uses
    | Authorization header value for authentication.
    |
    | Use this guard to authenticate mobile apps or web clients that cannot rely
    | on cookies/sessions.
    |
    */
    api: {
      driver: 'oat',

      /*
      |--------------------------------------------------------------------------
      | Tokens provider
      |--------------------------------------------------------------------------
      |
      | Uses SQL database for managing tokens. Use the "database" driver, when
      | tokens are the secondary mode of authentication.
      | For example: The Github personal tokens
      |
      | The foreignKey column is used to make the relationship between the user
      | and the token. You are free to use any column name here.
      |
      */
      tokenProvider: {
        type: 'api',
        driver: 'database',
        table: 'api_tokens',
        foreignKey: 'user_id',
      },

      provider: {
        /*
        |--------------------------------------------------------------------------
        | Driver
        |--------------------------------------------------------------------------
        |
        | Name of the driver
        |
        */
        driver: 'lucid',

        /*
        |--------------------------------------------------------------------------
        | Identifier key
        |--------------------------------------------------------------------------
        |
        | The identifier key is the unique key on the model. In most cases specifying
        | the primary key is the right choice.
        |
        */
        identifierKey: 'id',

        /*
        |--------------------------------------------------------------------------
        | Uids
        |--------------------------------------------------------------------------
        |
        | Uids are used to search a user against one of the mentioned columns. During
        | login, the auth module will search the user mentioned value against one
        | of the mentioned columns to find their user record.
        |
        */
        uids: ['email'],

        /*
        |--------------------------------------------------------------------------
        | Model
        |--------------------------------------------------------------------------
        |
        | The model to use for fetching or finding users. The model is imported
        | lazily since the config files are read way earlier in the lifecycle
        | of booting the app and the models may not be in a usable state at
        | that time.
        |
        */
        model: () => import('App/Models/User'),
      },
    },
  },
}

export default authConfig


================================================
FILE: config/bodyparser.ts
================================================
/**
 * Config source: https://git.io/Jfefn
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import type { BodyParserConfig } from '@ioc:Adonis/Core/BodyParser'

const bodyParserConfig: BodyParserConfig = {
  /*
  |--------------------------------------------------------------------------
  | White listed methods
  |--------------------------------------------------------------------------
  |
  | HTTP methods for which body parsing must be performed. It is a good practice
  | to avoid body parsing for `GET` requests.
  |
  */
  whitelistedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],

  /*
  |--------------------------------------------------------------------------
  | JSON parser settings
  |--------------------------------------------------------------------------
  |
  | The settings for the JSON parser. The types defines the request content
  | types which gets processed by the JSON parser.
  |
  */
  json: {
    encoding: 'utf-8',
    limit: '1mb',
    strict: true,
    types: [
      'application/json',
      'application/json-patch+json',
      'application/vnd.api+json',
      'application/csp-report',
    ],
  },

  /*
  |--------------------------------------------------------------------------
  | Form parser settings
  |--------------------------------------------------------------------------
  |
  | The settings for the `application/x-www-form-urlencoded` parser. The types
  | defines the request content types which gets processed by the form parser.
  |
  */
  form: {
    encoding: 'utf-8',
    limit: '1mb',
    queryString: {},

    /*
    |--------------------------------------------------------------------------
    | Convert empty strings to null
    |--------------------------------------------------------------------------
    |
    | Convert empty form fields to null. HTML forms results in field string
    | value when the field is left blank. This option normalizes all the blank
    | field values to "null"
    |
    */
    convertEmptyStringsToNull: true,

    types: [
      'application/x-www-form-urlencoded',
    ],
  },

  /*
  |--------------------------------------------------------------------------
  | Raw body parser settings
  |--------------------------------------------------------------------------
  |
  | Raw body just reads the request body stream as a plain text, which you
  | can process by hand. This must be used when request body type is not
  | supported by the body parser.
  |
  */
  raw: {
    encoding: 'utf-8',
    limit: '1mb',
    queryString: {},
    types: [
      'text/*',
    ],
  },

  /*
  |--------------------------------------------------------------------------
  | Multipart parser settings
  |--------------------------------------------------------------------------
  |
  | The settings for the `multipart/form-data` parser. The types defines the
  | request content types which gets processed by the form parser.
  |
  */
  multipart: {
    /*
    |--------------------------------------------------------------------------
    | Auto process
    |--------------------------------------------------------------------------
    |
    | The auto process option will process uploaded files and writes them to
    | the `tmp` folder. You can turn it off and then manually use the stream
    | to pipe stream to a different destination.
    |
    | It is recommended to keep `autoProcess=true`. Unless you are processing bigger
    | file sizes.
    |
    */
    autoProcess: true,

    /*
    |--------------------------------------------------------------------------
    | Files to be processed manually
    |--------------------------------------------------------------------------
    |
    | You can turn off `autoProcess` for certain routes by defining
    | routes inside the following array.
    |
    | NOTE: Make sure the route pattern starts with a leading slash.
    |
    | Correct
    | ```js
    | /projects/:id/file
    | ```
    |
    | Incorrect
    | ```js
    | projects/:id/file
    | ```
    */
    processManually: [],

    /*
    |--------------------------------------------------------------------------
    | Temporary file name
    |--------------------------------------------------------------------------
    |
    | When auto processing is on. We will use this method to compute the temporary
    | file name. AdonisJs will compute a unique `tmpPath` for you automatically,
    | However, you can also define your own custom method.
    |
    */
    // tmpFileName () {
    // },

    /*
    |--------------------------------------------------------------------------
    | Encoding
    |--------------------------------------------------------------------------
    |
    | Request body encoding
    |
    */
    encoding: 'utf-8',

    /*
    |--------------------------------------------------------------------------
    | Convert empty strings to null
    |--------------------------------------------------------------------------
    |
    | Convert empty form fields to null. HTML forms results in field string
    | value when the field is left blank. This option normalizes all the blank
    | field values to "null"
    |
    */
    convertEmptyStringsToNull: true,

    /*
    |--------------------------------------------------------------------------
    | Max Fields
    |--------------------------------------------------------------------------
    |
    | The maximum number of fields allowed in the request body. The field includes
    | text inputs and files both.
    |
    */
    maxFields: 1000,

    /*
    |--------------------------------------------------------------------------
    | Request body limit
    |--------------------------------------------------------------------------
    |
    | The total limit to the multipart body. This includes all request files
    | and fields data.
    |
    */
    limit: '20mb',

    /*
    |--------------------------------------------------------------------------
    | Types
    |--------------------------------------------------------------------------
    |
    | The types that will be considered and parsed as multipart body.
    |
    */
    types: [
      'multipart/form-data',
    ],
  },
}

export default bodyParserConfig


================================================
FILE: config/cors.ts
================================================
/**
 * Config source: https://git.io/JfefC
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import type { CorsConfig } from '@ioc:Adonis/Core/Cors'

const corsConfig: CorsConfig = {
  /*
  |--------------------------------------------------------------------------
  | Enabled
  |--------------------------------------------------------------------------
  |
  | A boolean to enable or disable CORS integration from your AdonisJs
  | application.
  |
  | Setting the value to `true` will enable the CORS for all HTTP request. However,
  | you can define a function to enable/disable it on per request basis as well.
  |
  */
  enabled: false,

  // You can also use a function that return true or false.
  // enabled: (request) => request.url().startsWith('/api')

  /*
  |--------------------------------------------------------------------------
  | Origin
  |--------------------------------------------------------------------------
  |
  | Set a list of origins to be allowed for `Access-Control-Allow-Origin`.
  | The value can be one of the following:
  |
  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
  |
  | Boolean (true)    - Allow current request origin.
  | Boolean (false)   - Disallow all.
  | String            - Comma separated list of allowed origins.
  | Array             - An array of allowed origins.
  | String (*)        - A wildcard (*) to allow all request origins.
  | Function          - Receives the current origin string and should return
  |                     one of the above values.
  |
  */
  origin: true,

  /*
  |--------------------------------------------------------------------------
  | Methods
  |--------------------------------------------------------------------------
  |
  | An array of allowed HTTP methods for CORS. The `Access-Control-Request-Method`
  | is checked against the following list.
  |
  | Following is the list of default methods. Feel free to add more.
  */
  methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'],

  /*
  |--------------------------------------------------------------------------
  | Headers
  |--------------------------------------------------------------------------
  |
  | List of headers to be allowed for `Access-Control-Allow-Headers` header.
  | The value can be one of the following:
  |
  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers
  |
  | Boolean(true)     - Allow all headers mentioned in `Access-Control-Request-Headers`.
  | Boolean(false)    - Disallow all headers.
  | String            - Comma separated list of allowed headers.
  | Array             - An array of allowed headers.
  | Function          - Receives the current header and should return one of the above values.
  |
  */
  headers: true,

  /*
  |--------------------------------------------------------------------------
  | Expose Headers
  |--------------------------------------------------------------------------
  |
  | A list of headers to be exposed by setting `Access-Control-Expose-Headers`.
  | header. By default following 6 simple response headers are exposed.
  |
  | Cache-Control
  | Content-Language
  | Content-Type
  | Expires
  | Last-Modified
  | Pragma
  |
  | In order to add more headers, simply define them inside the following array.
  |
  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
  |
  */
  exposeHeaders: [
    'cache-control',
    'content-language',
    'content-type',
    'expires',
    'last-modified',
    'pragma',
  ],

  /*
  |--------------------------------------------------------------------------
  | Credentials
  |--------------------------------------------------------------------------
  |
  | Toggle `Access-Control-Allow-Credentials` header. If value is set to `true`,
  | then header will be set, otherwise not.
  |
  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
  |
  */
  credentials: true,

  /*
  |--------------------------------------------------------------------------
  | MaxAge
  |--------------------------------------------------------------------------
  |
  | Define `Access-Control-Max-Age` header in seconds.
  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
  |
  */
  maxAge: 90,
}

export default corsConfig


================================================
FILE: config/database.ts
================================================
/**
 * Config source: https://git.io/JesV9
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import Env from '@ioc:Adonis/Core/Env'
import type { DatabaseConfig } from '@ioc:Adonis/Lucid/Database'

const databaseConfig: DatabaseConfig = {
  /*
  |--------------------------------------------------------------------------
  | Connection
  |--------------------------------------------------------------------------
  |
  | The primary connection for making database queries across the application
  | You can use any key from the `connections` object defined in this same
  | file.
  |
  */
  connection: Env.get('DB_CONNECTION'),

  connections: {
    /*
    |--------------------------------------------------------------------------
    | MySQL config
    |--------------------------------------------------------------------------
    |
    | Configuration for MySQL database. Make sure to install the driver
    | from npm when using this connection
    |
    | npm i mysql2
    |
    */
    mysql: {
      client: 'mysql2',
      connection: {
        host: Env.get('MYSQL_HOST'),
        port: Env.get('MYSQL_PORT'),
        user: Env.get('MYSQL_USER'),
        password: Env.get('MYSQL_PASSWORD', ''),
        database: Env.get('MYSQL_DB_NAME'),
      },
      migrations: {
        naturalSort: true,
      },
      healthCheck: false,
      debug: false,
    },

  }
}

export default databaseConfig


================================================
FILE: config/drive.ts
================================================
/**
 * Config source: https://git.io/JBt3o
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import Env from '@ioc:Adonis/Core/Env'
import { driveConfig } from '@adonisjs/core/build/config'
import Application from '@ioc:Adonis/Core/Application'

/*
|--------------------------------------------------------------------------
| Drive Config
|--------------------------------------------------------------------------
|
| The `DriveConfig` relies on the `DisksList` interface which is
| defined inside the `contracts` directory.
|
*/
export default driveConfig({
  /*
  |--------------------------------------------------------------------------
  | Default disk
  |--------------------------------------------------------------------------
  |
  | The default disk to use for managing file uploads. The value is driven by
  | the `DRIVE_DISK` environment variable.
  |
  */
  disk: Env.get('DRIVE_DISK'),

  disks: {
    /*
    |--------------------------------------------------------------------------
    | Local
    |--------------------------------------------------------------------------
    |
    | Uses the local file system to manage files. Make sure to turn off serving
    | files when not using this disk.
    |
    */
    local: {
      driver: 'local',
      visibility: 'public',

      /*
      |--------------------------------------------------------------------------
      | Storage root - Local driver only
      |--------------------------------------------------------------------------
      |
      | Define an absolute path to the storage directory from where to read the
      | files.
      |
      */
      root: Application.tmpPath('uploads'),

      /*
      |--------------------------------------------------------------------------
      | Serve files - Local driver only
      |--------------------------------------------------------------------------
      |
      | When this is set to true, AdonisJS will configure a files server to serve
      | files from the disk root. This is done to mimic the behavior of cloud
      | storage services that has inbuilt capabilities to serve files.
      |
      */
      serveFiles: true,

      /*
      |--------------------------------------------------------------------------
      | Base path - Local driver only
      |--------------------------------------------------------------------------
      |
      | Base path is always required when "serveFiles = true". Also make sure
      | the `basePath` is unique across all the disks using "local" driver and
      | you are not registering routes with this prefix.
      |
      */
      basePath: '/uploads',
    },

    /*
    |--------------------------------------------------------------------------
    | S3 Driver
    |--------------------------------------------------------------------------
    |
    | Uses the S3 cloud storage to manage files. Make sure to install the s3
    | drive separately when using it.
    |
    |**************************************************************************
    | npm i @adonisjs/drive-s3
    |**************************************************************************
    |
    */
    // s3: {
    //   driver: 's3',
    //   visibility: 'public',
    //   key: Env.get('S3_KEY'),
    //   secret: Env.get('S3_SECRET'),
    //   region: Env.get('S3_REGION'),
    //   bucket: Env.get('S3_BUCKET'),
    //   endpoint: Env.get('S3_ENDPOINT'),
    //
    //  // For minio to work
    //  // forcePathStyle: true,
    // },

    /*
    |--------------------------------------------------------------------------
    | GCS Driver
    |--------------------------------------------------------------------------
    |
    | Uses the Google cloud storage to manage files. Make sure to install the GCS
    | drive separately when using it.
    |
    |**************************************************************************
    | npm i @adonisjs/drive-gcs
    |**************************************************************************
    |
    */
    // gcs: {
    //   driver: 'gcs',
    //   visibility: 'public',
    //   keyFilename: Env.get('GCS_KEY_FILENAME'),
    //   bucket: Env.get('GCS_BUCKET'),

      /*
      |--------------------------------------------------------------------------
      | Uniform ACL - Google cloud storage only
      |--------------------------------------------------------------------------
      |
      | When using the Uniform ACL on the bucket, the "visibility" option is
      | ignored. Since, the files ACL is managed by the google bucket policies
      | directly.
      |
      |**************************************************************************
      | Learn more: https://cloud.google.com/storage/docs/uniform-bucket-level-access
      |**************************************************************************
      |
      | The following option just informs drive whether your bucket is using uniform
      | ACL or not. The actual setting needs to be toggled within the Google cloud
      | console.
      |
      */
    //   usingUniformAcl: false,
    // },
  },
})


================================================
FILE: config/hash.ts
================================================
/**
 * Config source: https://git.io/JfefW
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import Env from '@ioc:Adonis/Core/Env'
import { hashConfig } from '@adonisjs/core/build/config'

/*
|--------------------------------------------------------------------------
| Hash Config
|--------------------------------------------------------------------------
|
| The `HashConfig` relies on the `HashList` interface which is
| defined inside `contracts` directory.
|
*/
export default hashConfig({
  /*
  |--------------------------------------------------------------------------
  | Default hasher
  |--------------------------------------------------------------------------
  |
  | By default we make use of the argon hasher to hash values. However, feel
  | free to change the default value
  |
  */
  default: Env.get('HASH_DRIVER', 'scrypt'),

  list: {
    /*
    |--------------------------------------------------------------------------
    | scrypt
    |--------------------------------------------------------------------------
    |
    | Scrypt mapping uses the Node.js inbuilt crypto module for creating
    | hashes.
    |
    | We are using the default configuration recommended within the Node.js
    | documentation.
    | https://nodejs.org/api/crypto.html#cryptoscryptpassword-salt-keylen-options-callback
    |
    */
    scrypt: {
      driver: 'scrypt',
      cost: 16384,
      blockSize: 8,
      parallelization: 1,
      saltSize: 16,
      keyLength: 64,
      maxMemory: 32 * 1024 * 1024,
    },

    /*
    |--------------------------------------------------------------------------
    | Argon
    |--------------------------------------------------------------------------
    |
    | Argon mapping uses the `argon2` driver to hash values.
    |
    | Make sure you install the underlying dependency for this driver to work.
    | https://www.npmjs.com/package/phc-argon2.
    |
    | npm install phc-argon2
    |
    */
    argon: {
      driver: 'argon2',
      variant: 'id',
      iterations: 3,
      memory: 4096,
      parallelism: 1,
      saltSize: 16,
    },

    /*
    |--------------------------------------------------------------------------
    | Bcrypt
    |--------------------------------------------------------------------------
    |
    | Bcrypt mapping uses the `bcrypt` driver to hash values.
    |
    | Make sure you install the underlying dependency for this driver to work.
    | https://www.npmjs.com/package/phc-bcrypt.
    |
    | npm install phc-bcrypt
    |
    */
    bcrypt: {
      driver: 'bcrypt',
      rounds: 10,
    },
  },
})


================================================
FILE: contracts/ally.ts
================================================
/**
 * Contract source: https://git.io/JOdiQ
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

declare module '@ioc:Adonis/Addons/Ally' {
	interface SocialProviders {
    spotify: {
      config: SpotifyDriverConfig
      implementation: SpotifyDriverContract
    }
	}
}


================================================
FILE: contracts/auth.ts
================================================
/**
 * Contract source: https://git.io/JOdz5
 *
 * Feel free to let us know via PR, if you find something broken in this
 * file.
 */

import User from 'App/Models/User'

declare module '@ioc:Adonis/Addons/Auth' {
  /*
  |--------------------------------------------------------------------------
  | Providers
  |--------------------------------------------------------------------------
  |
  | The providers are used to fetch users. The Auth module comes pre-bundled
  | with two providers that are `Lucid` and `Database`. Both uses database
  | to fetch user details.
  |
  | You can also create and register your own custom providers.
  |
  */
  interface ProvidersList {
    /*
    |--------------------------------------------------------------------------
    | User Provider
    |--------------------------------------------------------------------------
    |
    | The following provider uses Lucid models as a driver for fetching user
    | details from the database for authentication.
    |
    | You can create multiple providers using the same underlying driver with
    | different Lucid models.
    |
    */
    user: {
      implementation: LucidProviderContract<typeof User>
      config: LucidProviderConfig<typeof User>
    }
  }

  /*
  |--------------------------------------------------------------------------
  | Guards
  |--------------------------------------------------------------------------
  |
  | The guards are used for authenticating users using different drivers.
  | The auth module comes with 3 different guards.
  |
  | - SessionGuardContract
  | - BasicAuthGuardContract
  | - OATGuardContract ( Opaque access token )
  |
  | Every guard needs a provider for looking up users from the database.
  |
  */
  interface GuardsList {
    /*
    |--------------------------------------------------------------------------
    | OAT Guard
    |--------------------------------------------------------------------------
    |
    | OAT, stands for (Opaque access tokens) guard uses database backed tokens
    | to authenticate requests.
    |
    */
    api: {
      implementation: OATGuardContract<'user', 'api'>
      config: OATGuardConfig<'user'>
      client: OATClientContract<'user'>
    }
  }
}


================================================
FILE: contracts/drive.ts
================================================
/**
 * Contract source: https://git.io/JBt3I
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

import type { InferDisksFromConfig } from '@adonisjs/core/build/config'
import type driveConfig from '../config/drive'

declare module '@ioc:Adonis/Core/Drive' {
  interface DisksList extends InferDisksFromConfig<typeof driveConfig> {}
}


================================================
FILE: contracts/env.ts
================================================
/**
 * Contract source: https://git.io/JTm6U
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

declare module '@ioc:Adonis/Core/Env' {
  /*
  |--------------------------------------------------------------------------
  | Getting types for validated environment variables
  |--------------------------------------------------------------------------
  |
  | The `default` export from the "../env.ts" file exports types for the
  | validated environment variables. Here we merge them with the `EnvTypes`
  | interface so that you can enjoy intellisense when using the "Env"
  | module.
  |
  */

  type CustomTypes = typeof import('../env').default
  interface EnvTypes extends CustomTypes {
  }
}


================================================
FILE: contracts/events.ts
================================================
/**
 * Contract source: https://git.io/JfefG
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

declare module '@ioc:Adonis/Core/Event' {
  /*
  |--------------------------------------------------------------------------
  | Define typed events
  |--------------------------------------------------------------------------
  |
  | You can define types for events inside the following interface and
  | AdonisJS will make sure that all listeners and emit calls adheres
  | to the defined types.
  |
  | For example:
  |
  | interface EventsList {
  |   'new:user': UserModel
  | }
  |
  | Now calling `Event.emit('new:user')` will statically ensure that passed value is
  | an instance of the the UserModel only.
  |
  */
  interface EventsList {
    //
  }
}


================================================
FILE: contracts/hash.ts
================================================
/**
 * Contract source: https://git.io/Jfefs
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

import type { InferListFromConfig } from '@adonisjs/core/build/config'
import type hashConfig from '../config/hash'

declare module '@ioc:Adonis/Core/Hash' {
  interface HashersList extends InferListFromConfig<typeof hashConfig> {}
}


================================================
FILE: contracts/tests.ts
================================================
/**
 * Contract source: https://bit.ly/3DP1ypf
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

import '@japa/runner'

declare module '@japa/runner' {
  interface TestContext {
    // Extend context
  }

  interface Test<TestData> {
    // Extend test
  }
}


================================================
FILE: database/factories/index.ts
================================================
// import Factory from '@ioc:Adonis/Lucid/Factory'


================================================
FILE: database/migrations/1698432448829_users.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'users'

  public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id').primary()
      table.string('name').notNullable()
      table.string('email', 255).notNullable().unique()
      // table.string('password', 180).notNullable()
      table.string('remember_me_token').nullable()
      table.string('access_token')
      table.string('provider')

      /**
       * Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true }).notNullable()
      table.timestamp('updated_at', { useTz: true }).notNullable()
    })
  }

  public async down() {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698432448832_api_tokens.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'api_tokens'

  public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id').primary()
      table.integer('user_id').unsigned()
      table.string('name').notNullable()
      table.string('type').notNullable()
      table.string('token', 64).notNullable().unique()

      /**
       * Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('expires_at', { useTz: true }).nullable()
      table.timestamp('created_at', { useTz: true }).notNullable()
    })
  }

  public async down() {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698440823891_social_tokens.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'social_tokens'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.integer('user_id').unsigned()
      table.text('token').notNullable()
      table.text('refresh_token').notNullable()
      table.dateTime('expires_at').notNullable()
      table.string('type', 50).notNullable()

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698491899562_genders.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'genders'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')

      table.string('name', 255)
      table.tinyint('status').defaultTo(1)

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698491899563_profiles.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'profiles'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')

      table.date('date_of_birth')
      table.text('description')
      table.string('avatar')
      table.integer('prefered_gender_id')
        .unsigned()


      table.integer('user_id')
        .unsigned()



      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698528332152_artists.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'artists'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')

      table.integer('user_id')
        .unsigned()


      /* user => track || null */
      table.string('type') 
 
      table.string('name')
      table.string('popularity')
      table.string('followers')
      table.string('uri')
      table.string('spotify_artist_id')
      table.string('artist_image')

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698570713652_genres.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
// "genres": [
//   "pakistani indie",
//   "pakistani pop",
//   "urdu hip hop"
// ],
export default class extends BaseSchema {
  protected tableName = 'genres'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.integer('artist_id')
        .unsigned()


      table.string('name')

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/migrations/1698571837102_tracks.ts
================================================
import BaseSchema from "@ioc:Adonis/Lucid/Schema";

export default class extends BaseSchema {
  protected tableName = "tracks";

  public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments("id");

      table.integer("user_id").unsigned();

      table.string("name");
      table.string("uri");
      table.string("popularity");
      table.string("tract_image");
      table.string("track_id");
      table.string("album");
      table.tinyint('favorite').defaultTo(0)
      
      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp("created_at", { useTz: true });
      table.timestamp("updated_at", { useTz: true });
    });
  }

  public async down() {
    this.schema.dropTable(this.tableName);
  }
}


================================================
FILE: database/migrations/1698942906705_matches.ts
================================================
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'matches'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')

      table.integer('matcher_user_id')
      table.integer('matched_user_id')
      table.date('match_date')

      table.tinyint("mutual_match").defaultTo(0)

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}


================================================
FILE: database/seeders/Gender.ts
================================================
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'
import Gender from 'App/Models/Gender'

export default class extends BaseSeeder {
  public async run () {
    await Gender.createMany([
      {
        name: 'Male',
      },
      {
        name: 'Female',
      },
      {
        name: 'Other',
      },
    ])
  }
}


================================================
FILE: env.ts
================================================
/*
|--------------------------------------------------------------------------
| Validating Environment Variables
|--------------------------------------------------------------------------
|
| In this file we define the rules for validating environment variables.
| By performing validation we ensure that your application is running in
| a stable environment with correct configuration values.
|
| This file is read automatically by the framework during the boot lifecycle
| and hence do not rename or move this file to a different location.
|
*/

import Env from '@ioc:Adonis/Core/Env'

export default Env.rules({
	HOST: Env.schema.string({ format: 'host' }),
	PORT: Env.schema.number(),
	APP_KEY: Env.schema.string(),
	APP_NAME: Env.schema.string(),
  DRIVE_DISK: Env.schema.enum(['local'] as const),
	NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const),
	DB_CONNECTION: Env.schema.string(),
	MYSQL_HOST: Env.schema.string({ format: 'host' }),
	MYSQL_PORT: Env.schema.number(),
	MYSQL_USER: Env.schema.string(),
	MYSQL_PASSWORD: Env.schema.string.optional(),
	MYSQL_DB_NAME: Env.schema.string(),
	SPOTIFY_CLIENT_ID: Env.schema.string(),
	SPOTIFY_CLIENT_SECRET: Env.schema.string(),
  SPOTIFY_CALLBACK_URL: Env.schema.string.optional(),
	SESSION_DRIVER: Env.schema.string()
})


================================================
FILE: package.json
================================================
{
  "name": "musical-matching",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "node ace serve --watch",
    "build": "node ace build --production",
    "start": "node server.js",
    "test": "node ace test"
  },
  "devDependencies": {
    "@adonisjs/assembler": "^5.9.6",
    "@japa/preset-adonis": "^1.2.0",
    "@japa/runner": "^2.5.1",
    "@types/proxy-addr": "^2.0.2",
    "@types/source-map-support": "^0.5.9",
    "adonis-preset-ts": "^2.1.0",
    "pino-pretty": "^10.2.3",
    "typescript": "~4.6",
    "youch": "^3.3.2",
    "youch-terminal": "^2.2.3"
  },
  "dependencies": {
    "@adonisjs/ally": "^4.1.5",
    "@adonisjs/auth": "^8.2.3",
    "@adonisjs/core": "^5.9.0",
    "@adonisjs/lucid": "^18.4.2",
    "@adonisjs/repl": "^3.1.11",
    "axios": "^1.6.0",
    "luxon": "^3.4.3",
    "mysql2": "^3.6.2",
    "proxy-addr": "^2.0.7",
    "reflect-metadata": "^0.1.13",
    "source-map-support": "^0.5.21"
  }
}


================================================
FILE: providers/AppProvider.ts
================================================
import type { ApplicationContract } from '@ioc:Adonis/Core/Application'

export default class AppProvider {
  constructor (protected app: ApplicationContract) {
  }

  public register () {
    // Register your own bindings
  }

  public async boot () {
    // IoC container is ready
  }

  public async ready () {
    // App is ready
  }

  public async shutdown () {
    // Cleanup, since app is going down
  }
}


================================================
FILE: server.ts
================================================
/*
|--------------------------------------------------------------------------
| AdonisJs Server
|--------------------------------------------------------------------------
|
| The contents in this file is meant to bootstrap the AdonisJs application
| and start the HTTP server to accept incoming connections. You must avoid
| making this file dirty and instead make use of `lifecycle hooks` provided
| by AdonisJs service providers for custom code.
|
*/

import 'reflect-metadata'
import sourceMapSupport from 'source-map-support'
import { Ignitor } from '@adonisjs/core/build/standalone'

sourceMapSupport.install({ handleUncaughtExceptions: false })

new Ignitor(__dirname)
  .httpServer()
  .start()


================================================
FILE: start/kernel.ts
================================================
/*
|--------------------------------------------------------------------------
| Application middleware
|--------------------------------------------------------------------------
|
| This file is used to define middleware for HTTP requests. You can register
| middleware as a `closure` or an IoC container binding. The bindings are
| preferred, since they keep this file clean.
|
*/

import Server from '@ioc:Adonis/Core/Server'

/*
|--------------------------------------------------------------------------
| Global middleware
|--------------------------------------------------------------------------
|
| An array of global middleware, that will be executed in the order they
| are defined for every HTTP requests.
|
*/
Server.middleware.register([
  () => import('@ioc:Adonis/Core/BodyParser'),
])

/*
|--------------------------------------------------------------------------
| Named middleware
|--------------------------------------------------------------------------
|
| Named middleware are defined as key-value pair. The value is the namespace
| or middleware function and key is the alias. Later you can use these
| alias on individual routes. For example:
|
| { auth: () => import('App/Middleware/Auth') }
|
| and then use it as follows
|
| Route.get('dashboard', 'UserController.dashboard').middleware('auth')
|
*/
Server.middleware.registerNamed({
  auth: () => import('App/Middleware/Auth')
})


================================================
FILE: start/routes.ts
================================================
/*
|--------------------------------------------------------------------------
| Routes
|--------------------------------------------------------------------------
|
| This file is dedicated for defining HTTP routes. A single file is enough
| for majority of projects, however you can define routes in different
| files and just make sure to import them inside this file. For example
|
| Define routes in following two files
| ├── start/routes/cart.ts
| ├── start/routes/customer.ts
|
| and then import them inside `start/routes.ts` as follows
|
| import './routes/cart'
| import './routes/customer'
|
*/

import Route from "@ioc:Adonis/Core/Route";

Route.get('health', ({ response }) => response.noContent())

Route.group(() => {
  Route.get("/", async ({ response }) => {
    return response.json("Working.");
  });

  // SIGN IN ROUTES
  Route.get("/signin", "UsersController.redirect");

  //OAuth CALLBACK
  Route.get("/signin-callback", "UsersController.handleCallback");

  Route.post("/logout", "UsersController.logout");

  Route.group(() => {
    Route.get("/", "ProfilesController.get");
    Route.post("/", "ProfilesController.store");
  })
  .prefix("profile")
  .middleware("auth:api");

  Route.group(() => {
    Route.get("/", "GendersController.index");
  })
  .prefix("genders")
  .middleware("auth:api");

  Route.group(() => {
    Route.get("/artists", "SpotifyController.artists");
    Route.get("/tracks", "SpotifyController.tracks");
    Route.get("/track-by-name", "SpotifyController.trackByName");
  })
  .prefix("spotify")
  .middleware("auth:api");

  Route.group(() => {
    /* potential matches */
    Route.get("/", "MatchesController.get");

    /* mark match */
    Route.post("/mutual", "MatchesController.mutualMatch");

    /* get mutual match history */
    Route.get("/history", "MatchesController.history");
  })
  .prefix("matches")
  .middleware("auth:api");

}).prefix("api");


================================================
FILE: test.ts
================================================
/*
|--------------------------------------------------------------------------
| Tests
|--------------------------------------------------------------------------
|
| The contents in this file boots the AdonisJS application and configures
| the Japa tests runner.
|
| For the most part you will never edit this file. The configuration
| for the tests can be controlled via ".adonisrc.json" and
| "tests/bootstrap.ts" files.
|
*/

process.env.NODE_ENV = 'test'

import 'reflect-metadata'
import sourceMapSupport from 'source-map-support'
import { Ignitor } from '@adonisjs/core/build/standalone'
import { configure, processCliArgs, run, RunnerHooksHandler } from '@japa/runner'

sourceMapSupport.install({ handleUncaughtExceptions: false })

const kernel = new Ignitor(__dirname).kernel('test')

kernel
  .boot()
  .then(() => import('./tests/bootstrap'))
  .then(({ runnerHooks, ...config }) => {
    const app: RunnerHooksHandler[] = [() => kernel.start()]

    configure({
      ...kernel.application.rcFile.tests,
      ...processCliArgs(process.argv.slice(2)),
      ...config,
      ...{
        importer: (filePath) => import(filePath),
        setup: app.concat(runnerHooks.setup),
        teardown: runnerHooks.teardown,
      },
      cwd: kernel.application.appRoot
    })

    run()
  })


================================================
FILE: tests/bootstrap.ts
================================================
/**
 * File source: https://bit.ly/3ukaHTz
 *
 * Feel free to let us know via PR, if you find something broken in this contract
 * file.
 */

import type { Config } from '@japa/runner'
import TestUtils from '@ioc:Adonis/Core/TestUtils'
import { assert, runFailedTests, specReporter, apiClient } from '@japa/preset-adonis'

/*
|--------------------------------------------------------------------------
| Japa Plugins
|--------------------------------------------------------------------------
|
| Japa plugins allows you to add additional features to Japa. By default
| we register the assertion plugin.
|
| Feel free to remove existing plugins or add more.
|
*/
export const plugins: Required<Config>['plugins'] = [assert(), runFailedTests(), apiClient()]

/*
|--------------------------------------------------------------------------
| Japa Reporters
|--------------------------------------------------------------------------
|
| Japa reporters displays/saves the progress of tests as they are executed.
| By default, we register the spec reporter to show a detailed report
| of tests on the terminal.
|
*/
export const reporters: Required<Config>['reporters'] = [specReporter()]

/*
|--------------------------------------------------------------------------
| Runner hooks
|--------------------------------------------------------------------------
|
| Runner hooks are executed after booting the AdonisJS app and
| before the test files are imported.
|
| You can perform actions like starting the HTTP server or running migrations
| within the runner hooks
|
*/
export const runnerHooks: Pick<Required<Config>, 'setup' | 'teardown'> = {
  setup: [() => TestUtils.ace().loadCommands()],
  teardown: [],
}

/*
|--------------------------------------------------------------------------
| Configure individual suites
|--------------------------------------------------------------------------
|
| The configureSuite method gets called for every test suite registered
| within ".adonisrc.json" file.
|
| You can use this method to configure suites. For example: Only start
| the HTTP server when it is a functional suite.
*/
export const configureSuite: Required<Config>['configureSuite'] = (suite) => {
  if (suite.name === 'functional') {
    suite.setup(() => TestUtils.httpServer().start())
  }
}


================================================
FILE: tests/functional/hello_world.spec.ts
================================================
import { test } from '@japa/runner'

test('display welcome page', async ({ client }) => {
  const response = await client.get('/')

  response.assertStatus(200)
  response.assertBodyContains({ hello: 'world' })
})


================================================
FILE: tsconfig.json
================================================
{
  "extends": "adonis-preset-ts/tsconfig.json",
  "include": [
    "**/*"
  ],
  "exclude": [
    "node_modules",
    "build"
  ],
  "compilerOptions": {
    "outDir": "build",
    "rootDir": "./",
    "sourceMap": true,
    "paths": {
      "App/*": [
        "./app/*"
      ],
      "Config/*": [
        "./config/*"
      ],
      "Contracts/*": [
        "./contracts/*"
      ],
      "Database/*": [
        "./database/*"
      ]
    },
    "types": [
      "@adonisjs/core",
      "@adonisjs/repl",
      "@japa/preset-adonis/build/adonis-typings",
      "@adonisjs/lucid",
      "@adonisjs/ally",
      "@adonisjs/auth"
    ]
  }
}
Download .txt
gitextract_bylbss8l/

├── .adonisrc.json
├── .dockerignore
├── .editorconfig
├── .github/
│   └── workflows/
│       └── build.yml
├── .gitignore
├── Dockerfile
├── README.md
├── ace
├── ace-manifest.json
├── app/
│   ├── Controllers/
│   │   └── Http/
│   │       ├── GendersController.ts
│   │       ├── MatchesController.ts
│   │       ├── ProfilesController.ts
│   │       ├── SpotifyController.ts
│   │       └── UsersController.ts
│   ├── Exceptions/
│   │   └── Handler.ts
│   ├── Middleware/
│   │   ├── Auth.ts
│   │   └── SilentAuth.ts
│   ├── Models/
│   │   ├── ApiToken.ts
│   │   ├── Artist.ts
│   │   ├── Gender.ts
│   │   ├── Genre.ts
│   │   ├── Match.ts
│   │   ├── Profile.ts
│   │   ├── SocialToken.ts
│   │   ├── Track.ts
│   │   └── User.ts
│   ├── Services/
│   │   └── SpotifyService.ts
│   └── Validators/
│       ├── CreateMatchValidator.ts
│       └── CreateProfileValidator.ts
├── commands/
│   └── index.ts
├── compose.yml
├── config/
│   ├── ally.ts
│   ├── app.ts
│   ├── auth.ts
│   ├── bodyparser.ts
│   ├── cors.ts
│   ├── database.ts
│   ├── drive.ts
│   └── hash.ts
├── contracts/
│   ├── ally.ts
│   ├── auth.ts
│   ├── drive.ts
│   ├── env.ts
│   ├── events.ts
│   ├── hash.ts
│   └── tests.ts
├── database/
│   ├── factories/
│   │   └── index.ts
│   ├── migrations/
│   │   ├── 1698432448829_users.ts
│   │   ├── 1698432448832_api_tokens.ts
│   │   ├── 1698440823891_social_tokens.ts
│   │   ├── 1698491899562_genders.ts
│   │   ├── 1698491899563_profiles.ts
│   │   ├── 1698528332152_artists.ts
│   │   ├── 1698570713652_genres.ts
│   │   ├── 1698571837102_tracks.ts
│   │   └── 1698942906705_matches.ts
│   └── seeders/
│       └── Gender.ts
├── env.ts
├── package.json
├── providers/
│   └── AppProvider.ts
├── server.ts
├── start/
│   ├── kernel.ts
│   └── routes.ts
├── test.ts
├── tests/
│   ├── bootstrap.ts
│   └── functional/
│       └── hello_world.spec.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (81 symbols across 38 files)

FILE: app/Controllers/Http/GendersController.ts
  class GendersController (line 5) | class GendersController {
    method index (line 6) | public async index({ response }: HttpContextContract) {

FILE: app/Controllers/Http/MatchesController.ts
  class MatchesController (line 8) | class MatchesController {
    method get (line 10) | public async get({ response, auth }: HttpContextContract) {
    method mutualMatch (line 49) | public async mutualMatch({ request, response, auth }: HttpContextContr...
    method history (line 115) | public async history({ response, auth }: HttpContextContract) {

FILE: app/Controllers/Http/ProfilesController.ts
  class ProfilesController (line 7) | class ProfilesController {
    method get (line 8) | public async get({ response, auth }: HttpContextContract) {
    method store (line 27) | public async store({ request, response, auth }: HttpContextContract) {

FILE: app/Controllers/Http/SpotifyController.ts
  class SpotifiesController (line 4) | class SpotifiesController {
    method artists (line 5) | public async artists({ response, auth }: HttpContextContract) {
    method tracks (line 16) | public async tracks({ response, auth }: HttpContextContract) {
    method trackByName (line 39) | public async trackByName({request, response, auth}: HttpContextContrac...

FILE: app/Controllers/Http/UsersController.ts
  class UsersController (line 8) | class UsersController {
    method redirect (line 9) | public async redirect({ ally }: HttpContextContract) {
    method handleCallback (line 13) | public async handleCallback({ ally, auth, response }: HttpContextContr...
    method logout (line 120) | public async logout({ auth, response }: HttpContextContract) {

FILE: app/Exceptions/Handler.ts
  class ExceptionHandler (line 19) | class ExceptionHandler extends HttpExceptionHandler {
    method constructor (line 20) | constructor () {

FILE: app/Middleware/Auth.ts
  class AuthMiddleware (line 12) | class AuthMiddleware {
    method authenticate (line 26) | protected async authenticate(auth: HttpContextContract['auth'], guards...
    method handle (line 63) | public async handle (

FILE: app/Middleware/SilentAuth.ts
  class SilentAuthMiddleware (line 9) | class SilentAuthMiddleware {
    method handle (line 13) | public async handle({ auth }: HttpContextContract, next: () => Promise...

FILE: app/Models/ApiToken.ts
  class ApiToken (line 4) | class ApiToken extends BaseModel {

FILE: app/Models/Artist.ts
  class Artist (line 5) | class Artist extends BaseModel {

FILE: app/Models/Gender.ts
  class Gender (line 4) | class Gender extends BaseModel {

FILE: app/Models/Genre.ts
  class Genre (line 4) | class Genre extends BaseModel {

FILE: app/Models/Match.ts
  class Match (line 4) | class Match extends BaseModel {

FILE: app/Models/Profile.ts
  class Profile (line 5) | class Profile extends BaseModel {

FILE: app/Models/SocialToken.ts
  class SocialToken (line 4) | class SocialToken extends BaseModel {

FILE: app/Models/Track.ts
  class Track (line 5) | class Track extends BaseModel {

FILE: app/Models/User.ts
  class User (line 8) | class User extends BaseModel {

FILE: app/Services/SpotifyService.ts
  class SpotifyService (line 9) | class SpotifyService {
    method getArtists (line 10) | public static async getArtists(userId) {
    method getTracks (line 23) | public static async getTracks(userId) {
    method saveArtists (line 37) | public static async saveArtists(userId, artists) {
    method saveTracks (line 71) | public static async saveTracks(userId, tracks) {
    method getTracksByIds (line 94) | public static async getTracksByIds(userId, trackIds) {
    method getTracksData (line 110) | public static getTracksData(tracks) {
    method updateFavorityTrack (line 123) | public static async updateFavorityTrack(userId, trackIds) {
    method getTracksByName (line 143) | public static async getTracksByName(userId, name) {

FILE: app/Validators/CreateMatchValidator.ts
  class CreateMatchValidator (line 4) | class CreateMatchValidator {
    method constructor (line 5) | constructor(protected ctx: HttpContextContract) {}

FILE: app/Validators/CreateProfileValidator.ts
  class CreateProfileValidator (line 4) | class CreateProfileValidator {
    method constructor (line 5) | constructor(protected ctx: HttpContextContract) {}

FILE: contracts/ally.ts
  type SocialProviders (line 9) | interface SocialProviders {

FILE: contracts/auth.ts
  type ProvidersList (line 23) | interface ProvidersList {
  type GuardsList (line 57) | interface GuardsList {

FILE: contracts/drive.ts
  type DisksList (line 12) | interface DisksList extends InferDisksFromConfig<typeof driveConfig> {}

FILE: contracts/env.ts
  type CustomTypes (line 21) | type CustomTypes = typeof import('../env').default
  type EnvTypes (line 22) | interface EnvTypes extends CustomTypes {

FILE: contracts/events.ts
  type EventsList (line 28) | interface EventsList {

FILE: contracts/hash.ts
  type HashersList (line 12) | interface HashersList extends InferListFromConfig<typeof hashConfig> {}

FILE: contracts/tests.ts
  type TestContext (line 11) | interface TestContext {
  type Test (line 15) | interface Test<TestData> {

FILE: database/migrations/1698432448829_users.ts
  method up (line 6) | public async up() {
  method down (line 24) | public async down() {

FILE: database/migrations/1698432448832_api_tokens.ts
  method up (line 6) | public async up() {
  method down (line 22) | public async down() {

FILE: database/migrations/1698440823891_social_tokens.ts
  method up (line 6) | public async up () {
  method down (line 23) | public async down () {

FILE: database/migrations/1698491899562_genders.ts
  method up (line 6) | public async up () {
  method down (line 21) | public async down () {

FILE: database/migrations/1698491899563_profiles.ts
  method up (line 6) | public async up () {
  method down (line 30) | public async down () {

FILE: database/migrations/1698528332152_artists.ts
  method up (line 6) | public async up () {
  method down (line 32) | public async down () {

FILE: database/migrations/1698570713652_genres.ts
  method up (line 10) | public async up () {
  method down (line 27) | public async down () {

FILE: database/migrations/1698571837102_tracks.ts
  method up (line 6) | public async up() {
  method down (line 28) | public async down() {

FILE: database/migrations/1698942906705_matches.ts
  method up (line 6) | public async up () {
  method down (line 24) | public async down () {

FILE: database/seeders/Gender.ts
  method run (line 5) | public async run () {

FILE: providers/AppProvider.ts
  class AppProvider (line 3) | class AppProvider {
    method constructor (line 4) | constructor (protected app: ApplicationContract) {
    method register (line 7) | public register () {
    method boot (line 11) | public async boot () {
    method ready (line 15) | public async ready () {
    method shutdown (line 19) | public async shutdown () {
Condensed preview — 67 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (118K chars).
[
  {
    "path": ".adonisrc.json",
    "chars": 865,
    "preview": "{\n  \"typescript\": true,\n  \"commands\": [\n    \"./commands\",\n    \"@adonisjs/core/build/commands/index.js\",\n    \"@adonisjs/r"
  },
  {
    "path": ".dockerignore",
    "chars": 13,
    "preview": "node_modules\n"
  },
  {
    "path": ".editorconfig",
    "chars": 227,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 1593,
    "preview": "name: Build\n\non:\n  push:\n    branches:\n      - main\n\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: ${{ github.repository }}\n\njo"
  },
  {
    "path": ".gitignore",
    "chars": 55,
    "preview": "node_modules\nbuild\ncoverage\n.vscode\n.DS_STORE\n.env\ntmp\n"
  },
  {
    "path": "Dockerfile",
    "chars": 745,
    "preview": "FROM node:20-alpine3.18 as base\n\nRUN apk --no-cache add curl\n\n# All deps stage\nFROM base as deps\nWORKDIR /app\nADD packag"
  },
  {
    "path": "README.md",
    "chars": 2797,
    "preview": "# Music Match API with AdonisJS\n\nAn innovative application that connects people based on their musical preferences, draw"
  },
  {
    "path": "ace",
    "chars": 467,
    "preview": "/*\n|--------------------------------------------------------------------------\n| Ace Commands\n|-------------------------"
  },
  {
    "path": "ace-manifest.json",
    "chars": 16363,
    "preview": "{\n  \"commands\": {\n    \"dump:rcfile\": {\n      \"settings\": {},\n      \"commandPath\": \"@adonisjs/core/build/commands/DumpRc\""
  },
  {
    "path": "app/Controllers/Http/GendersController.ts",
    "chars": 537,
    "preview": "import type { HttpContextContract } from \"@ioc:Adonis/Core/HttpContext\";\n\nimport Gender from \"App/Models/Gender\";\n\nexpor"
  },
  {
    "path": "app/Controllers/Http/MatchesController.ts",
    "chars": 3962,
    "preview": "import type { HttpContextContract } from \"@ioc:Adonis/Core/HttpContext\";\nimport { schema } from '@ioc:Adonis/Core/Valida"
  },
  {
    "path": "app/Controllers/Http/ProfilesController.ts",
    "chars": 2233,
    "preview": "import type { HttpContextContract } from \"@ioc:Adonis/Core/HttpContext\";\n\nimport Profile from \"App/Models/Profile\";\nimpo"
  },
  {
    "path": "app/Controllers/Http/SpotifyController.ts",
    "chars": 1938,
    "preview": "import type { HttpContextContract } from \"@ioc:Adonis/Core/HttpContext\";\nimport SpotifyService from \"App/Services/Spotif"
  },
  {
    "path": "app/Controllers/Http/UsersController.ts",
    "chars": 3527,
    "preview": "import type { HttpContextContract } from \"@ioc:Adonis/Core/HttpContext\";\nimport Artist from \"App/Models/Artist\";\nimport "
  },
  {
    "path": "app/Exceptions/Handler.ts",
    "chars": 741,
    "preview": "/*\n|--------------------------------------------------------------------------\n| Http Exception Handler\n|---------------"
  },
  {
    "path": "app/Middleware/Auth.ts",
    "chars": 2271,
    "preview": "import { AuthenticationException } from '@adonisjs/auth/build/standalone'\nimport type { GuardsList } from '@ioc:Adonis/A"
  },
  {
    "path": "app/Middleware/SilentAuth.ts",
    "chars": 628,
    "preview": "import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'\n\n/**\n * Silent auth middleware can be used as a "
  },
  {
    "path": "app/Models/ApiToken.ts",
    "chars": 391,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'\n\nexport default class ApiToke"
  },
  {
    "path": "app/Models/Artist.ts",
    "chars": 743,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, HasMany, column, hasMany } from '@ioc:Adonis/Lucid/Orm'\nimport Genr"
  },
  {
    "path": "app/Models/Gender.ts",
    "chars": 388,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'\n\nexport default class Gender "
  },
  {
    "path": "app/Models/Genre.ts",
    "chars": 426,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'\n\nexport default class Genre e"
  },
  {
    "path": "app/Models/Match.ts",
    "chars": 521,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'\n\nexport default class Match e"
  },
  {
    "path": "app/Models/Profile.ts",
    "chars": 680,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, HasOne, column, hasOne } from '@ioc:Adonis/Lucid/Orm'\nimport Gender"
  },
  {
    "path": "app/Models/SocialToken.ts",
    "chars": 550,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'\n\nexport default class SocialT"
  },
  {
    "path": "app/Models/Track.ts",
    "chars": 781,
    "preview": "import { DateTime } from 'luxon'\nimport { BaseModel, HasMany, column, hasMany } from '@ioc:Adonis/Lucid/Orm'\nimport Arti"
  },
  {
    "path": "app/Models/User.ts",
    "chars": 1173,
    "preview": "import { DateTime } from 'luxon'\nimport Hash from '@ioc:Adonis/Core/Hash'\nimport { column, beforeSave, BaseModel, hasMan"
  },
  {
    "path": "app/Services/SpotifyService.ts",
    "chars": 4101,
    "preview": "import Env from \"@ioc:Adonis/Core/Env\";\n\nimport Artist from \"App/Models/Artist\";\nimport Genre from \"App/Models/Genre\";\ni"
  },
  {
    "path": "app/Validators/CreateMatchValidator.ts",
    "chars": 1371,
    "preview": "import { schema } from '@ioc:Adonis/Core/Validator'\nimport { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'\n\n"
  },
  {
    "path": "app/Validators/CreateProfileValidator.ts",
    "chars": 1619,
    "preview": "import { schema } from '@ioc:Adonis/Core/Validator'\nimport { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'\n\n"
  },
  {
    "path": "commands/index.ts",
    "chars": 754,
    "preview": "import { listDirectoryFiles } from '@adonisjs/core/build/standalone'\nimport Application from '@ioc:Adonis/Core/Applicati"
  },
  {
    "path": "compose.yml",
    "chars": 250,
    "preview": "services:\n  db:\n    image: mysql:8.0\n    platform: linux/x86_64\n    ports:\n      - 3306:3306\n    volumes:\n      - mysql-"
  },
  {
    "path": "config/ally.ts",
    "chars": 1093,
    "preview": "/**\n * Config source: https://git.io/JOdi5\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/app.ts",
    "chars": 7516,
    "preview": "/**\n * Config source: https://git.io/JfefZ\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/auth.ts",
    "chars": 3606,
    "preview": "/**\n * Config source: https://git.io/JY0mp\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/bodyparser.ts",
    "chars": 6293,
    "preview": "/**\n * Config source: https://git.io/Jfefn\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/cors.ts",
    "chars": 4415,
    "preview": "/**\n * Config source: https://git.io/JfefC\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/database.ts",
    "chars": 1457,
    "preview": "/**\n * Config source: https://git.io/JesV9\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/drive.ts",
    "chars": 5161,
    "preview": "/**\n * Config source: https://git.io/JBt3o\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "config/hash.ts",
    "chars": 2650,
    "preview": "/**\n * Config source: https://git.io/JfefW\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "contracts/ally.ts",
    "chars": 320,
    "preview": "/**\n * Contract source: https://git.io/JOdiQ\n *\n * Feel free to let us know via PR, if you find something broken in this"
  },
  {
    "path": "contracts/auth.ts",
    "chars": 2239,
    "preview": "/**\n * Contract source: https://git.io/JOdz5\n *\n * Feel free to let us know via PR, if you find something broken in this"
  },
  {
    "path": "contracts/drive.ts",
    "chars": 382,
    "preview": "/**\n * Contract source: https://git.io/JBt3I\n *\n * Feel free to let us know via PR, if you find something broken in this"
  },
  {
    "path": "contracts/env.ts",
    "chars": 746,
    "preview": "/**\n * Contract source: https://git.io/JTm6U\n *\n * Feel free to let us know via PR, if you find something broken in this"
  },
  {
    "path": "contracts/events.ts",
    "chars": 807,
    "preview": "/**\n * Contract source: https://git.io/JfefG\n *\n * Feel free to let us know via PR, if you find something broken in this"
  },
  {
    "path": "contracts/hash.ts",
    "chars": 378,
    "preview": "/**\n * Contract source: https://git.io/Jfefs\n *\n * Feel free to let us know via PR, if you find something broken in this"
  },
  {
    "path": "contracts/tests.ts",
    "chars": 308,
    "preview": "/**\n * Contract source: https://bit.ly/3DP1ypf\n *\n * Feel free to let us know via PR, if you find something broken in th"
  },
  {
    "path": "database/factories/index.ts",
    "chars": 51,
    "preview": "// import Factory from '@ioc:Adonis/Lucid/Factory'\n"
  },
  {
    "path": "database/migrations/1698432448829_users.ts",
    "chars": 814,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'us"
  },
  {
    "path": "database/migrations/1698432448832_api_tokens.ts",
    "chars": 728,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'ap"
  },
  {
    "path": "database/migrations/1698440823891_social_tokens.ts",
    "chars": 744,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'so"
  },
  {
    "path": "database/migrations/1698491899562_genders.ts",
    "chars": 590,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'ge"
  },
  {
    "path": "database/migrations/1698491899563_profiles.ts",
    "chars": 728,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'pr"
  },
  {
    "path": "database/migrations/1698528332152_artists.ts",
    "chars": 825,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'ar"
  },
  {
    "path": "database/migrations/1698570713652_genres.ts",
    "chars": 681,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n// \"genres\": [\n//   \"pakistani indie\",\n//   \"pakistani pop\",\n//   \"urd"
  },
  {
    "path": "database/migrations/1698571837102_tracks.ts",
    "chars": 799,
    "preview": "import BaseSchema from \"@ioc:Adonis/Lucid/Schema\";\n\nexport default class extends BaseSchema {\n  protected tableName = \"t"
  },
  {
    "path": "database/migrations/1698942906705_matches.ts",
    "chars": 674,
    "preview": "import BaseSchema from '@ioc:Adonis/Lucid/Schema'\n\nexport default class extends BaseSchema {\n  protected tableName = 'ma"
  },
  {
    "path": "database/seeders/Gender.ts",
    "chars": 319,
    "preview": "import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'\nimport Gender from 'App/Models/Gender'\n\nexport default class extends B"
  },
  {
    "path": "env.ts",
    "chars": 1302,
    "preview": "/*\n|--------------------------------------------------------------------------\n| Validating Environment Variables\n|-----"
  },
  {
    "path": "package.json",
    "chars": 945,
    "preview": "{\n  \"name\": \"musical-matching\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"node ace serve --wat"
  },
  {
    "path": "providers/AppProvider.ts",
    "chars": 414,
    "preview": "import type { ApplicationContract } from '@ioc:Adonis/Core/Application'\n\nexport default class AppProvider {\n  constructo"
  },
  {
    "path": "server.ts",
    "chars": 704,
    "preview": "/*\n|--------------------------------------------------------------------------\n| AdonisJs Server\n|----------------------"
  },
  {
    "path": "start/kernel.ts",
    "chars": 1413,
    "preview": "/*\n|--------------------------------------------------------------------------\n| Application middleware\n|---------------"
  },
  {
    "path": "start/routes.ts",
    "chars": 1919,
    "preview": "/*\n|--------------------------------------------------------------------------\n| Routes\n|-------------------------------"
  },
  {
    "path": "test.ts",
    "chars": 1299,
    "preview": "/*\n|--------------------------------------------------------------------------\n| Tests\n|--------------------------------"
  },
  {
    "path": "tests/bootstrap.ts",
    "chars": 2304,
    "preview": "/**\n * File source: https://bit.ly/3ukaHTz\n *\n * Feel free to let us know via PR, if you find something broken in this c"
  },
  {
    "path": "tests/functional/hello_world.spec.ts",
    "chars": 214,
    "preview": "import { test } from '@japa/runner'\n\ntest('display welcome page', async ({ client }) => {\n  const response = await clien"
  },
  {
    "path": "tsconfig.json",
    "chars": 643,
    "preview": "{\n  \"extends\": \"adonis-preset-ts/tsconfig.json\",\n  \"include\": [\n    \"**/*\"\n  ],\n  \"exclude\": [\n    \"node_modules\",\n    \""
  }
]

About this extraction

This page contains the full source code of the Benjamin-Code-YouTube/boumboum-back GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 67 files (105.7 KB), approximately 25.0k tokens, and a symbol index with 81 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!