commit 053f62bb18609b5ba74d7f37e6e4ac49cbc6c4a4 Author: loicbersier Date: Tue Dec 3 21:18:40 2019 +0100 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9142239 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# editorconfig.org +root = true + +[*] +indent_size = 2 +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..04f24b8 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +HOST=localhost +PORT=3333 +NODE_ENV=development +APP_URL=http://${HOST}:${PORT} +CACHE_VIEWS=false +APP_KEY= +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=root +DB_PASSWORD= +DB_DATABASE=adonis +SESSION_DRIVER=cookie +HASH_DRIVER=bcrypt +auth=test \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21db896 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Node modules +node_modules +package-lock.json + +# Adonis directory for storing tmp files +tmp + +# Environment variables, never commit this file +.env + +# The development sqlite file +database/development.sqlite + +# VSCode & Webstorm history directories +.history +.idea + +# MacOS useless file +.DS_Store + +# video files +public/uploads/*.mp4 +public/uploads/*.mp3 +public/uploads/*.flac + +# Thumbnail +public/Thumbnail/*.* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..197f6ab --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Jeff downloader + +Jeff downloader is a website to download from [hundreds](https://ytdl-org.github.io/youtube-dl/supportedsites.html) of website using [youtube-dl](https://ytdl-org.github.io/youtube-dl/index.html) + +You can find a hosted version on https://namejeff.xyz/ \ No newline at end of file diff --git a/ace b/ace new file mode 100644 index 0000000..271a604 --- /dev/null +++ b/ace @@ -0,0 +1,21 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Ace Commands +|-------------------------------------------------------------------------- +| +| The ace file is just a regular Javascript file but with no extension. You +| can call `node ace` followed by the command name and it just works. +| +| Also you can use `adonis` followed by the command name, since the adonis +| global proxy all the ace commands. +| +*/ + +const { Ignitor } = require('@adonisjs/ignitor') + +new Ignitor(require('@adonisjs/fold')) + .appRoot(__dirname) + .fireAce() + .catch(console.error) diff --git a/app/Controllers/Http/DownloadController.js b/app/Controllers/Http/DownloadController.js new file mode 100644 index 0000000..5d1a580 --- /dev/null +++ b/app/Controllers/Http/DownloadController.js @@ -0,0 +1,179 @@ +'use strict' +const youtubedl = require('youtube-dl') +const fs = require('fs') +const ffmpeg = require('fluent-ffmpeg') +const timestamp = require('time-stamp') + +let viewCounter = 0; +let files = []; +let day; +let month; +let announcement = 'Twitter download seems to work fine now!'; + +class DownloadController { + + async index ({ view, response }) { + // Get date for some event + let today = new Date(); + day = today.getDay(); + month = today.getMonth(); + + viewCounter++; + if (response.request.url == '/legacy') return view.render('legacy', { title: 'le epic downloader v0.11.3', viewCounter: viewCounter, day: day, month: month, announcement: announcement}); + + files = []; + let file = [] + for (let f of fs.readdirSync('./public/uploads')) { + file.push(f) + } + // get the 5 most recent files + file = file.sort(function(a, b) { + if ((a || b).endsWith('.mp4') && !(a || b).startsWith('HIDE')) { + let time1 = fs.statSync(`./public/uploads/${b}`).ctime; + let time2 = fs.statSync(`./public/uploads/${a}`).ctime; + if (time1 < time2) return -1; + if (time1 > time2) return 1; + } + return 0; + }).slice(0, 5) + + file.forEach((file) => { + // If mp4 and is not to be hidden from the recent feed + if (file.endsWith('.mp4') && !file.startsWith('HIDE')) { + let fileInfo = fs.statSync(`./public/uploads/${file}`); + // Take screenshot at the first frame of the mp4 file + ffmpeg(`./public/uploads/${file}`) + .takeScreenshots({ count: 1, timemarks: [ 1 ], size: '720x480', filename: file + '.png' }, 'public/thumbnail') + .on('error', (err) => { + console.error(err); + return; + }); + + if (fs.existsSync(`./public/thumbnail/${file}.png`)) { + // Get the image as base64 + let imgData = fs.readFileSync(`./public/thumbnail/${file}.png`).toString('base64'); + if (imgData) { + // Send file name, file size in MB relative path for the file and base64 of the image + files.push({ name: file, size: (fileInfo.size / 1000000.0).toFixed(2), location: `uploads/${file}`, img: imgData }); + fs.unlinkSync(`./public/thumbnail/${file}.png`); + } + } + // If mp3 or flac and not to be hidden from the recent feed + } else if ((file.endsWith('.mp3') || file.endsWith('.flac')) && !file.startsWith('HIDE')) { + let fileInfo = fs.statSync(`./public/uploads/${file}`); + // Send file name, file size in MB relative path for the file and base64 of music.png + files.push({ name: file, size: (fileInfo.size / 1000000.0).toFixed(2), location: `uploads/${file}`, img: fs.readFileSync(`./public/asset/music.png`).toString('base64') }); + } + }); + return view.render('index', { title: 'le epic downloader v0.11.3', viewCounter: viewCounter, file: files, day: day, month: month, announcement: announcement }); + } + + async download({ view, request, response }) { + let page = 'index'; + if (response.request.url == '/legacy') page = 'legacy'; + // To be honest i forgot what it does, but i think i need it + response.implicitEnd = false + + let option, DLFile + // Get form input + let data = { + url: request.input('URL'), + quality: request.input('quality'), + format: request.input('format'), + alt: request.input('alt'), + feed: request.input('feed') + } + + if (!data.url) { + return view.render(page, { + title: 'le epic downloader v0.11.3', + viewCounter: viewCounter, + file: files, + day: day, month: month, announcement: announcement , + error: true, + errormsg: 'bruh moment, you didin\'t input a link.' + }); + } + + // Youtube-dl quality settings + if (data.quality == 'small') + option = 'worst' + else + option = 'best' + + // If alt download ( Quality settings and file format option doesn't work here ) + if (data.alt) { + if (fs.existsSync('./public/uploads/alt.mp4')) { + fs.unlink('./public/uploads/alt.mp4', (err) => { + if (err); + }); + } + + return youtubedl.exec(data.url, ['--format=mp4', '-o', `public/uploads/alt.mp4`], {}, function(err, output) { + if (err) { + return view.render(page, { + title: 'le epic downloader v0.11.3', + viewCounter: viewCounter, + file: files, + day: day, month: month, announcement: announcement , + error: true, + errormsg: 'bruh moment, you didin\'t input a valid link.' + }); + } + + return response.attachment('./public/uploads/alt.mp4'); + }); + } else { + // Download as mp4 + let video = youtubedl(data.url, ['--format=mp4', '-f', option]); + + video.on('error', function(err) { + console.error(err) + return view.render(page, { + title: 'le epic downloader v0.11.3', + viewCounter: viewCounter, + file: files, + day: day, month: month, announcement: announcement , + error: true, + errormsg: 'bruh moment, you didin\'t input a valid link.' + }); + }) + + video.on('info', function(info) { + // Set file name + let title = info.title; + DLFile = `${timestamp('DD_MM_YYYY')}${title.slice(0,10).replace(/\s/g, '_')}.${info.ext}`; + // If no title use the ID + if (title == '_') title = `_${info.id}`; + // If user want to hide from the feed + if (data.feed == 'on') + DLFile = `HIDE${timestamp('DD_MM_YYYY')}${title.slice(0,10).replace(/\s/g, '_')}.${info.ext}`; + + DLFile = DLFile.replace(/[()]/g, '_'); + video.pipe(fs.createWriteStream(`./public/uploads/${DLFile}`)); + }); + + video.on('end', function() { + if (data.format == 'mp4') { + // If user requested mp4 directly attach the file + return response.attachment(`./public/uploads/${DLFile}`) + } else { + // If user requested an audio format, convert it + ffmpeg(`./public/uploads/${DLFile}`) + .noVideo() + .audioChannels('2') + .audioFrequency('44100') + .audioBitrate('320k') + .format(data.format) + .save(`./public/uploads/${DLFile}.${data.format}`) + .on('end', () => { + fs.unlinkSync(`./public/uploads/${DLFile}`); + return response.attachment(`./public/uploads/${DLFile}.${data.format}`); + }) + } + }); + } + } +} + +module.exports = DownloadController diff --git a/app/Middleware/ConvertEmptyStringsToNull.js b/app/Middleware/ConvertEmptyStringsToNull.js new file mode 100644 index 0000000..a5750cc --- /dev/null +++ b/app/Middleware/ConvertEmptyStringsToNull.js @@ -0,0 +1,17 @@ +'use strict' + +class ConvertEmptyStringsToNull { + async handle ({ request }, next) { + if (Object.keys(request.body).length) { + request.body = Object.assign( + ...Object.keys(request.body).map(key => ({ + [key]: request.body[key] !== '' ? request.body[key] : null + })) + ) + } + + await next() + } +} + +module.exports = ConvertEmptyStringsToNull diff --git a/app/Models/Traits/NoTimestamp.js b/app/Models/Traits/NoTimestamp.js new file mode 100644 index 0000000..58c9340 --- /dev/null +++ b/app/Models/Traits/NoTimestamp.js @@ -0,0 +1,16 @@ +'use strict' + +class NoTimestamp { + register (Model) { + Object.defineProperties(Model, { + createdAtColumn: { + get: () => null, + }, + updatedAtColumn: { + get: () => null, + }, + }) + } +} + +module.exports = NoTimestamp diff --git a/app/Models/User.js b/app/Models/User.js new file mode 100644 index 0000000..2804a44 --- /dev/null +++ b/app/Models/User.js @@ -0,0 +1,39 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Hash')} */ +const Hash = use('Hash') + +/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */ +const Model = use('Model') + +class User extends Model { + static boot () { + super.boot() + + /** + * A hook to hash the user password before saving + * it to the database. + */ + this.addHook('beforeSave', async (userInstance) => { + if (userInstance.dirty.password) { + userInstance.password = await Hash.make(userInstance.password) + } + }) + } + + /** + * A relationship on tokens is required for auth to + * work. Since features like `refreshTokens` or + * `rememberToken` will be saved inside the + * tokens table. + * + * @method tokens + * + * @return {Object} + */ + tokens () { + return this.hasMany('App/Models/Token') + } +} + +module.exports = User diff --git a/config/app.js b/config/app.js new file mode 100644 index 0000000..42c63a3 --- /dev/null +++ b/config/app.js @@ -0,0 +1,243 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Env')} */ +const Env = use('Env') + +module.exports = { + + /* + |-------------------------------------------------------------------------- + | Application Name + |-------------------------------------------------------------------------- + | + | This value is the name of your application and can be used when you + | need to place the application's name in a email, view or + | other location. + | + */ + + name: Env.get('APP_NAME', 'AdonisJs'), + + /* + |-------------------------------------------------------------------------- + | App Key + |-------------------------------------------------------------------------- + | + | App key is a randomly generated 16 or 32 characters long string required + | to encrypted cookies, sessions and other sensitive data. + | + */ + appKey: Env.getOrFail('APP_KEY'), + + http: { + /* + |-------------------------------------------------------------------------- + | Allow Method Spoofing + |-------------------------------------------------------------------------- + | + | Method spoofing allows you to make requests by spoofing the http verb. + | Which means you can make a GET request but instruct the server to + | treat as a POST or PUT request. If you want this feature, set the + | below value to true. + | + */ + allowMethodSpoofing: true, + + /* + |-------------------------------------------------------------------------- + | Trust Proxy + |-------------------------------------------------------------------------- + | + | Trust proxy defines whether X-Forwarded-* headers should be trusted or not. + | When your application is behind a proxy server like nginx, these values + | are set automatically and should be trusted. Apart from setting it + | to true or false Adonis supports a handful of ways to allow proxy + | values. Read documentation for that. + | + */ + trustProxy: false, + + /* + |-------------------------------------------------------------------------- + | Subdomains + |-------------------------------------------------------------------------- + | + | Offset to be used for returning subdomains for a given request. For + | majority of applications it will be 2, until you have nested + | sudomains. + | cheatsheet.adonisjs.com - offset - 2 + | virk.cheatsheet.adonisjs.com - offset - 3 + | + */ + subdomainOffset: 2, + + /* + |-------------------------------------------------------------------------- + | JSONP Callback + |-------------------------------------------------------------------------- + | + | Default jsonp callback to be used when callback query string is missing + | in request url. + | + */ + jsonpCallback: 'callback', + + + /* + |-------------------------------------------------------------------------- + | Etag + |-------------------------------------------------------------------------- + | + | Set etag on all HTTP responses. In order to disable for selected routes, + | you can call the `response.send` with an options object as follows. + | + | response.send('Hello', { ignoreEtag: true }) + | + */ + etag: false + }, + + views: { + /* + |-------------------------------------------------------------------------- + | Cache Views + |-------------------------------------------------------------------------- + | + | Define whether or not to cache the compiled view. Set it to true in + | production to optimize view loading time. + | + */ + cache: Env.get('CACHE_VIEWS', true) + }, + + static: { + /* + |-------------------------------------------------------------------------- + | Dot Files + |-------------------------------------------------------------------------- + | + | Define how to treat dot files when trying to serve static resources. + | By default it is set to ignore, which will pretend that dotfiles + | do not exist. + | + | Can be one of the following + | ignore, deny, allow + | + */ + dotfiles: 'ignore', + + /* + |-------------------------------------------------------------------------- + | ETag + |-------------------------------------------------------------------------- + | + | Enable or disable etag generation + | + */ + etag: true, + + /* + |-------------------------------------------------------------------------- + | Extensions + |-------------------------------------------------------------------------- + | + | Set file extension fallbacks. When set, if a file is not found, the given + | extensions will be added to the file name and search for. The first + | that exists will be served. Example: ['html', 'htm']. + | + */ + extensions: false + }, + + locales: { + /* + |-------------------------------------------------------------------------- + | Loader + |-------------------------------------------------------------------------- + | + | The loader to be used for fetching and updating locales. Below is the + | list of available options. + | + | file, database + | + */ + loader: 'file', + + /* + |-------------------------------------------------------------------------- + | Default Locale + |-------------------------------------------------------------------------- + | + | Default locale to be used by Antl provider. You can always switch drivers + | in runtime or use the official Antl middleware to detect the driver + | based on HTTP headers/query string. + | + */ + locale: 'en' + }, + + logger: { + /* + |-------------------------------------------------------------------------- + | Transport + |-------------------------------------------------------------------------- + | + | Transport to be used for logging messages. You can have multiple + | transports using same driver. + | + | Available drivers are: `file` and `console`. + | + */ + transport: 'console', + + /* + |-------------------------------------------------------------------------- + | Console Transport + |-------------------------------------------------------------------------- + | + | Using `console` driver for logging. This driver writes to `stdout` + | and `stderr` + | + */ + console: { + driver: 'console', + name: 'adonis-app', + level: 'info' + }, + + /* + |-------------------------------------------------------------------------- + | File Transport + |-------------------------------------------------------------------------- + | + | File transport uses file driver and writes log messages for a given + | file inside `tmp` directory for your app. + | + | For a different directory, set an absolute path for the filename. + | + */ + file: { + driver: 'file', + name: 'adonis-app', + filename: 'adonis.log', + level: 'info' + } + }, + + /* + |-------------------------------------------------------------------------- + | Generic Cookie Options + |-------------------------------------------------------------------------- + | + | The following cookie options are generic settings used by AdonisJs to create + | cookies. However, some parts of the application like `sessions` can have + | seperate settings for cookies inside `config/session.js`. + | + */ + cookie: { + httpOnly: true, + sameSite: false, + path: '/', + maxAge: 7200 + } +} diff --git a/config/auth.js b/config/auth.js new file mode 100644 index 0000000..5fceb35 --- /dev/null +++ b/config/auth.js @@ -0,0 +1,94 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Env')} */ +const Env = use('Env') + +module.exports = { + /* + |-------------------------------------------------------------------------- + | Authenticator + |-------------------------------------------------------------------------- + | + | Authentication is a combination of serializer and scheme with extra + | config to define on how to authenticate a user. + | + | Available Schemes - basic, session, jwt, api + | Available Serializers - lucid, database + | + */ + authenticator: 'session', + + /* + |-------------------------------------------------------------------------- + | Session + |-------------------------------------------------------------------------- + | + | Session authenticator makes use of sessions to authenticate a user. + | Session authentication is always persistent. + | + */ + session: { + serializer: 'lucid', + model: 'App/Models/User', + scheme: 'session', + uid: 'email', + password: 'password' + }, + + /* + |-------------------------------------------------------------------------- + | Basic Auth + |-------------------------------------------------------------------------- + | + | The basic auth authenticator uses basic auth header to authenticate a + | user. + | + | NOTE: + | This scheme is not persistent and users are supposed to pass + | login credentials on each request. + | + */ + basic: { + serializer: 'lucid', + model: 'App/Models/User', + scheme: 'basic', + uid: 'email', + password: 'password' + }, + + /* + |-------------------------------------------------------------------------- + | Jwt + |-------------------------------------------------------------------------- + | + | The jwt authenticator works by passing a jwt token on each HTTP request + | via HTTP `Authorization` header. + | + */ + jwt: { + serializer: 'lucid', + model: 'App/Models/User', + scheme: 'jwt', + uid: 'email', + password: 'password', + options: { + secret: Env.get('APP_KEY') + } + }, + + /* + |-------------------------------------------------------------------------- + | Api + |-------------------------------------------------------------------------- + | + | The Api scheme makes use of API personal tokens to authenticate a user. + | + */ + api: { + serializer: 'lucid', + model: 'App/Models/User', + scheme: 'api', + uid: 'email', + password: 'password' + } +} diff --git a/config/bodyParser.js b/config/bodyParser.js new file mode 100644 index 0000000..6b40f1a --- /dev/null +++ b/config/bodyParser.js @@ -0,0 +1,157 @@ +'use strict' + +module.exports = { + /* + |-------------------------------------------------------------------------- + | JSON Parser + |-------------------------------------------------------------------------- + | + | Below settings are applied when request body contains JSON payload. If + | you want body parser to ignore JSON payload, then simply set `types` + | to an empty array. + */ + json: { + /* + |-------------------------------------------------------------------------- + | limit + |-------------------------------------------------------------------------- + | + | Defines the limit of JSON that can be sent by the client. If payload + | is over 1mb it will not be processed. + | + */ + limit: '1mb', + + /* + |-------------------------------------------------------------------------- + | strict + |-------------------------------------------------------------------------- + | + | When `scrict` is set to true, body parser will only parse Arrays and + | Object. Otherwise everything parseable by `JSON.parse` is parsed. + | + */ + strict: true, + + /* + |-------------------------------------------------------------------------- + | types + |-------------------------------------------------------------------------- + | + | Which content types are processed as JSON payloads. You are free to + | add your own types here, but the request body should be parseable + | by `JSON.parse` method. + | + */ + types: [ + 'application/json', + 'application/json-patch+json', + 'application/vnd.api+json', + 'application/csp-report' + ] + }, + + /* + |-------------------------------------------------------------------------- + | Raw Parser + |-------------------------------------------------------------------------- + | + | + | + */ + raw: { + types: [ + 'text/*' + ] + }, + + /* + |-------------------------------------------------------------------------- + | Form Parser + |-------------------------------------------------------------------------- + | + | + | + */ + form: { + types: [ + 'application/x-www-form-urlencoded' + ] + }, + + /* + |-------------------------------------------------------------------------- + | Files Parser + |-------------------------------------------------------------------------- + | + | + | + */ + files: { + types: [ + 'multipart/form-data' + ], + + /* + |-------------------------------------------------------------------------- + | Max Size + |-------------------------------------------------------------------------- + | + | Below value is the max size of all the files uploaded to the server. It + | is validated even before files have been processed and hard exception + | is thrown. + | + | Consider setting a reasonable value here, otherwise people may upload GB's + | of files which will keep your server busy. + | + | Also this value is considered when `autoProcess` is set to true. + | + */ + maxSize: '20mb', + + /* + |-------------------------------------------------------------------------- + | Auto Process + |-------------------------------------------------------------------------- + | + | Whether or not to auto-process files. Since HTTP servers handle files via + | couple of specific endpoints. It is better to set this value off and + | manually process the files when required. + | + | This value can contain a boolean or an array of route patterns + | to be autoprocessed. + */ + autoProcess: true, + + /* + |-------------------------------------------------------------------------- + | Process Manually + |-------------------------------------------------------------------------- + | + | The list of routes that should not process files and instead rely on + | manual process. This list should only contain routes when autoProcess + | is to true. Otherwise everything is processed manually. + | + */ + processManually: [] + + /* + |-------------------------------------------------------------------------- + | Temporary file name + |-------------------------------------------------------------------------- + | + | Define a function, which should return a string to be used as the + | tmp file name. + | + | If not defined, Bodyparser will use `uuid` as the tmp file name. + | + | To be defined as. If you are defining the function, then do make sure + | to return a value from it. + | + | tmpFileName () { + | return 'some-unique-value' + | } + | + */ + } +} diff --git a/config/cors.js b/config/cors.js new file mode 100644 index 0000000..4c3848e --- /dev/null +++ b/config/cors.js @@ -0,0 +1,87 @@ +'use strict' + +module.exports = { + /* + |-------------------------------------------------------------------------- + | Origin + |-------------------------------------------------------------------------- + | + | Set a list of origins to be allowed. The value can be one of the following + | + | Boolean: true - Allow current request origin + | Boolean: false - Disallow all + | String - Comma seperated list of allowed origins + | Array - An array of allowed origins + | String: * - A wildcard to allow current request origin + | Function - Receives the current origin and should return one of the above values. + | + */ + origin: false, + + /* + |-------------------------------------------------------------------------- + | Methods + |-------------------------------------------------------------------------- + | + | HTTP methods to be allowed. The value can be one of the following + | + | String - Comma seperated list of allowed methods + | Array - An array of allowed methods + | + */ + methods: ['GET', 'PUT', 'PATCH', 'POST', 'DELETE'], + + /* + |-------------------------------------------------------------------------- + | Headers + |-------------------------------------------------------------------------- + | + | List of headers to be allowed via Access-Control-Request-Headers header. + | The value can be on of the following. + | + | Boolean: true - Allow current request headers + | Boolean: false - Disallow all + | String - Comma seperated list of allowed headers + | Array - An array of allowed headers + | String: * - A wildcard to allow current request 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 via `Access-Control-Expose-Headers` + | header. The value can be on of the following. + | + | Boolean: false - Disallow all + | String: Comma seperated list of allowed headers + | Array - An array of allowed headers + | + */ + exposeHeaders: false, + + /* + |-------------------------------------------------------------------------- + | Credentials + |-------------------------------------------------------------------------- + | + | Define Access-Control-Allow-Credentials header. It should always be a + | boolean. + | + */ + credentials: false, + + /* + |-------------------------------------------------------------------------- + | MaxAge + |-------------------------------------------------------------------------- + | + | Define Access-Control-Allow-Max-Age + | + */ + maxAge: 90 +} diff --git a/config/database.js b/config/database.js new file mode 100644 index 0000000..e9cb916 --- /dev/null +++ b/config/database.js @@ -0,0 +1,81 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Env')} */ +const Env = use('Env') + +/** @type {import('@adonisjs/ignitor/src/Helpers')} */ +const Helpers = use('Helpers') + +module.exports = { + /* + |-------------------------------------------------------------------------- + | Default Connection + |-------------------------------------------------------------------------- + | + | Connection defines the default connection settings to be used while + | interacting with SQL databases. + | + */ + connection: Env.get('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Sqlite + |-------------------------------------------------------------------------- + | + | Sqlite is a flat file database and can be good choice under development + | environment. + | + | npm i --save sqlite3 + | + */ + sqlite: { + client: 'sqlite3', + connection: { + filename: Helpers.databasePath(`${Env.get('DB_DATABASE', 'development')}.sqlite`) + }, + useNullAsDefault: true + }, + + /* + |-------------------------------------------------------------------------- + | MySQL + |-------------------------------------------------------------------------- + | + | Here we define connection settings for MySQL database. + | + | npm i --save mysql + | + */ + mysql: { + client: 'mysql', + connection: { + host: Env.get('DB_HOST', 'localhost'), + port: Env.get('DB_PORT', ''), + user: Env.get('DB_USER', 'root'), + password: Env.get('DB_PASSWORD', ''), + database: Env.get('DB_DATABASE', 'adonis') + } + }, + + /* + |-------------------------------------------------------------------------- + | PostgreSQL + |-------------------------------------------------------------------------- + | + | Here we define connection settings for PostgreSQL database. + | + | npm i --save pg + | + */ + pg: { + client: 'pg', + connection: { + host: Env.get('DB_HOST', 'localhost'), + port: Env.get('DB_PORT', ''), + user: Env.get('DB_USER', 'root'), + password: Env.get('DB_PASSWORD', ''), + database: Env.get('DB_DATABASE', 'adonis') + } + } +} diff --git a/config/hash.js b/config/hash.js new file mode 100644 index 0000000..42f5805 --- /dev/null +++ b/config/hash.js @@ -0,0 +1,49 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Env')} */ +const Env = use('Env') + +module.exports = { + /* + |-------------------------------------------------------------------------- + | Driver + |-------------------------------------------------------------------------- + | + | Driver to be used for hashing values. The same driver is used by the + | auth module too. + | + */ + driver: Env.get('HASH_DRIVER', 'bcrypt'), + + /* + |-------------------------------------------------------------------------- + | Bcrypt + |-------------------------------------------------------------------------- + | + | Config related to bcrypt hashing. https://www.npmjs.com/package/bcrypt + | package is used internally. + | + */ + bcrypt: { + rounds: 10 + }, + + /* + |-------------------------------------------------------------------------- + | Argon + |-------------------------------------------------------------------------- + | + | Config related to argon. https://www.npmjs.com/package/argon2 package is + | used internally. + | + | Since argon is optional, you will have to install the dependency yourself + | + |============================================================================ + | npm i argon2 + |============================================================================ + | + */ + argon: { + type: 1 + } +} diff --git a/config/session.js b/config/session.js new file mode 100644 index 0000000..03aec1a --- /dev/null +++ b/config/session.js @@ -0,0 +1,95 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Env')} */ +const Env = use('Env') + +module.exports = { + /* + |-------------------------------------------------------------------------- + | Session Driver + |-------------------------------------------------------------------------- + | + | The session driver to be used for storing session values. It can be + | cookie, file or redis. + | + | For `redis` driver, make sure to install and register `@adonisjs/redis` + | + */ + driver: Env.get('SESSION_DRIVER', 'cookie'), + + /* + |-------------------------------------------------------------------------- + | Cookie Name + |-------------------------------------------------------------------------- + | + | The name of the cookie to be used for saving session id. Session ids + | are signed and encrypted. + | + */ + cookieName: 'adonis-session', + + /* + |-------------------------------------------------------------------------- + | Clear session when browser closes + |-------------------------------------------------------------------------- + | + | If this value is true, the session cookie will be temporary and will be + | removed when browser closes. + | + */ + clearWithBrowser: true, + + /* + |-------------------------------------------------------------------------- + | Session age + |-------------------------------------------------------------------------- + | + | This value is only used when `clearWithBrowser` is set to false. The + | age must be a valid https://npmjs.org/package/ms string or should + | be in milliseconds. + | + | Valid values are: + | '2h', '10d', '5y', '2.5 hrs' + | + */ + age: '2h', + + /* + |-------------------------------------------------------------------------- + | Cookie options + |-------------------------------------------------------------------------- + | + | Cookie options defines the options to be used for setting up session + | cookie + | + */ + cookie: { + httpOnly: true, + sameSite: false, + path: '/' + }, + + /* + |-------------------------------------------------------------------------- + | Sessions location + |-------------------------------------------------------------------------- + | + | If driver is set to file, we need to define the relative location from + | the temporary path or absolute url to any location. + | + */ + file: { + location: 'sessions' + }, + + /* + |-------------------------------------------------------------------------- + | Redis config + |-------------------------------------------------------------------------- + | + | The configuration for the redis driver. By default we reference it from + | the redis file. But you are free to define an object here too. + | + */ + redis: 'self::redis.local' +} diff --git a/config/shield.js b/config/shield.js new file mode 100644 index 0000000..255cee3 --- /dev/null +++ b/config/shield.js @@ -0,0 +1,145 @@ +'use strict' + +module.exports = { + /* + |-------------------------------------------------------------------------- + | Content Security Policy + |-------------------------------------------------------------------------- + | + | Content security policy filters out the origins not allowed to execute + | and load resources like scripts, styles and fonts. There are wide + | variety of options to choose from. + */ + csp: { + /* + |-------------------------------------------------------------------------- + | Directives + |-------------------------------------------------------------------------- + | + | All directives are defined in camelCase and here is the list of + | available directives and their possible values. + | + | https://content-security-policy.com + | + | @example + | directives: { + | defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com'] + | } + | + */ + directives: { + }, + /* + |-------------------------------------------------------------------------- + | Report only + |-------------------------------------------------------------------------- + | + | Setting `reportOnly=true` will not block the scripts from running and + | instead report them to a URL. + | + */ + reportOnly: false, + /* + |-------------------------------------------------------------------------- + | Set all headers + |-------------------------------------------------------------------------- + | + | Headers staring with `X` have been depreciated, since all major browsers + | supports the standard CSP header. So its better to disable deperciated + | headers, unless you want them to be set. + | + */ + setAllHeaders: false, + + /* + |-------------------------------------------------------------------------- + | Disable on android + |-------------------------------------------------------------------------- + | + | Certain versions of android are buggy with CSP policy. So you can set + | this value to true, to disable it for Android versions with buggy + | behavior. + | + | Here is an issue reported on a different package, but helpful to read + | if you want to know the behavior. https://github.com/helmetjs/helmet/pull/82 + | + */ + disableAndroid: true + }, + + /* + |-------------------------------------------------------------------------- + | X-XSS-Protection + |-------------------------------------------------------------------------- + | + | X-XSS Protection saves applications from XSS attacks. It is adopted + | by IE and later followed by some other browsers. + | + | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection + | + */ + xss: { + enabled: true, + enableOnOldIE: false + }, + + /* + |-------------------------------------------------------------------------- + | Iframe Options + |-------------------------------------------------------------------------- + | + | xframe defines whether or not your website can be embedded inside an + | iframe. Choose from one of the following options. + | @available options + | DENY, SAMEORIGIN, ALLOW-FROM http://example.com + | + | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options + */ + xframe: 'DENY', + + /* + |-------------------------------------------------------------------------- + | No Sniff + |-------------------------------------------------------------------------- + | + | Browsers have a habit of sniffing content-type of a response. Which means + | files with .txt extension containing Javascript code will be executed as + | Javascript. You can disable this behavior by setting nosniff to false. + | + | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options + | + */ + nosniff: true, + + /* + |-------------------------------------------------------------------------- + | No Open + |-------------------------------------------------------------------------- + | + | IE users can execute webpages in the context of your website, which is + | a serious security risk. Below option will manage this for you. + | + */ + noopen: true, + + /* + |-------------------------------------------------------------------------- + | CSRF Protection + |-------------------------------------------------------------------------- + | + | CSRF Protection adds another layer of security by making sure, actionable + | routes does have a valid token to execute an action. + | + */ + csrf: { + enable: true, + methods: ['POST', 'PUT', 'DELETE'], + filterUris: [], + cookieOptions: { + httpOnly: false, + sameSite: true, + path: '/', + maxAge: 7200 + } + } +} diff --git a/database/factory.js b/database/factory.js new file mode 100644 index 0000000..16b5084 --- /dev/null +++ b/database/factory.js @@ -0,0 +1,21 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Factory +|-------------------------------------------------------------------------- +| +| Factories are used to define blueprints for database tables or Lucid +| models. Later you can use these blueprints to seed your database +| with dummy data. +| +*/ + +/** @type {import('@adonisjs/lucid/src/Factory')} */ +// const Factory = use('Factory') + +// Factory.blueprint('App/Models/User', (faker) => { +// return { +// username: faker.username() +// } +// }) diff --git a/package.json b/package.json new file mode 100644 index 0000000..3ff8b89 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "adonis-fullstack-app", + "version": "0.11.3", + "adonis-version": "4.1.0", + "description": "A video downloader based on youtube-dl", + "main": "index.js", + "scripts": { + "start": "node server.js", + "test": "node ace test" + }, + "keywords": [ + "adonisjs", + "adonis-app" + ], + "author": "Loïc Bersier", + "license": "", + "private": true, + "dependencies": { + "@adonisjs/ace": "^5.0.8", + "@adonisjs/auth": "^3.0.7", + "@adonisjs/bodyparser": "^2.0.9", + "@adonisjs/cors": "^1.0.7", + "@adonisjs/fold": "^4.0.9", + "@adonisjs/framework": "^5.0.9", + "@adonisjs/ignitor": "^2.0.8", + "@adonisjs/lucid": "^6.1.3", + "@adonisjs/session": "^1.0.27", + "@adonisjs/shield": "^1.0.8", + "@adonisjs/validator": "^5.0.6", + "fluent-ffmpeg": "^2.1.2", + "mysql": "^2.17.1", + "node-fetch": "^2.6.0", + "time-stamp": "^2.2.0", + "youtube-dl": "^1.13.1" + }, + "devDependencies": {}, + "autoload": { + "App": "./app" + } +} diff --git a/public/JS/snow.js b/public/JS/snow.js new file mode 100644 index 0000000..0d8e0e8 --- /dev/null +++ b/public/JS/snow.js @@ -0,0 +1,99 @@ +/*! +// Snow.js - v0.0.3 +// kurisubrooks.com +*/ + +// Amount of Snowflakes +var snowMax = 35; + +// Snowflake Colours +var snowColor = ["#DDD", "#EEE"]; + +// Snow Entity +var snowEntity = "•"; + +// Falling Velocity +var snowSpeed = 1; + +// Minimum Flake Size +var snowMinSize = 12; + +// Maximum Flake Size +var snowMaxSize = 42; + +// Refresh Rate (in milliseconds) +var snowRefresh = 50; + +// Additional Styles +var snowStyles = "cursor: default; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none;"; + +/* +// End of Configuration +// ---------------------------------------- +// Do not modify the code below this line +*/ + +var snow = [], + pos = [], + coords = [], + lefr = [], + marginBottom, + marginRight; + +function randomise(range) { + rand = Math.floor(range * Math.random()); + return rand; +} + +function initSnow() { + var snowSize = snowMaxSize - snowMinSize; + marginBottom = document.body.scrollHeight - 5; + marginRight = document.body.clientWidth - 15; + + for (i = 0; i <= snowMax; i++) { + coords[i] = 0; + lefr[i] = Math.random() * 15; + pos[i] = 0.03 + Math.random() / 10; + snow[i] = document.getElementById("flake" + i); + snow[i].style.fontFamily = "inherit"; + snow[i].size = randomise(snowSize) + snowMinSize; + snow[i].style.fontSize = snow[i].size + "px"; + snow[i].style.color = snowColor[randomise(snowColor.length)]; + snow[i].style.zIndex = 1000; + snow[i].sink = snowSpeed * snow[i].size / 5; + snow[i].posX = randomise(marginRight - snow[i].size); + snow[i].posY = randomise(2 * marginBottom - marginBottom - 2 * snow[i].size); + snow[i].style.left = snow[i].posX + "px"; + snow[i].style.top = snow[i].posY + "px"; + } + + moveSnow(); +} + +function resize() { + marginBottom = document.body.scrollHeight - 5; + marginRight = document.body.clientWidth - 15; +} + +function moveSnow() { + for (i = 0; i <= snowMax; i++) { + coords[i] += pos[i]; + snow[i].posY += snow[i].sink; + snow[i].style.left = snow[i].posX + lefr[i] * Math.sin(coords[i]) + "px"; + snow[i].style.top = snow[i].posY + "px"; + + if (snow[i].posY >= marginBottom - 2 * snow[i].size || parseInt(snow[i].style.left) > (marginRight - 3 * lefr[i])) { + snow[i].posX = randomise(marginRight - snow[i].size); + snow[i].posY = 0; + } + } + + setTimeout("moveSnow()", snowRefresh); +} + +for (i = 0; i <= snowMax; i++) { + document.write("" + snowEntity + ""); +} + +window.addEventListener('resize', resize); +window.addEventListener('load', initSnow); diff --git a/public/asset/favicon.ico b/public/asset/favicon.ico new file mode 100644 index 0000000..ea4f069 Binary files /dev/null and b/public/asset/favicon.ico differ diff --git a/public/asset/jeff.png b/public/asset/jeff.png new file mode 100644 index 0000000..e8bbd49 Binary files /dev/null and b/public/asset/jeff.png differ diff --git a/public/asset/music.png b/public/asset/music.png new file mode 100644 index 0000000..7b203e5 Binary files /dev/null and b/public/asset/music.png differ diff --git a/public/css/index.css b/public/css/index.css new file mode 100644 index 0000000..dce1384 --- /dev/null +++ b/public/css/index.css @@ -0,0 +1,102 @@ +.downloadbtn { + animation: colorchange 13s infinite; + animation-direction: alternate-reverse; +} + +a:link { + color: #A0FF9A; +} + +a:visited { + color: #A7FF56; +} + +a:hover { + color: #DCFFA5 !important; +} + +.checkbox:hover, .radio:hover { + color: lightgray !important; +} + +.gradientBG { + background: rgb(59,44,207); + background: linear-gradient(90deg, rgba(59,44,207,1) 0%, rgba(140,41,151,1) 100%); +} + +/* Grow */ +.hvr-grow { + display: inline-block; + vertical-align: middle; + transform: translateZ(0); + box-shadow: 0 0 1px rgba(0, 0, 0, 0); + backface-visibility: hidden; + -moz-osx-font-smoothing: grayscale; + transition-duration: 0.3s; + transition-property: transform; +} + +.hvr-grow:hover, +.hvr-grow:focus, +.hvr-grow:active { + transform: scale(1.1); +} + +.fadein { + animation: fadein 2s; +} + +.fadeout { + animation: fadeout 2s; +} + +@keyframes fadein { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes fadeout { + from { opacity: 1; } + to { opacity: 0; } +} + +@keyframes colorchange { + 0% { + background-color: #ff2400; + color: white; + } + 10% { + background-color: #e81d1d; + } + 20% { + background-color: #e8b71d; + color: black; + } + 30% { + background-color: #e3e81d; +} + 40% { + background-color: #1de840; + color: white; + } + 50% { + background-color: #1ddde8; + color: black; + } + 60% { + background-color: #2b1de8; + color: white; + } + 70% { + background-color: #dd00f3; + } + 80% { + background-color: #dd00f3; + } + 90% { + background-color: #ff2400; + } + 100% { + background-color: #ff2400; + } +} diff --git a/public/css/legacy.css b/public/css/legacy.css new file mode 100644 index 0000000..46d7752 --- /dev/null +++ b/public/css/legacy.css @@ -0,0 +1,145 @@ +body { + background-color: #262d2c; + color: white; + text-align: center; + align-content: center; + font-family: "Roboto", "Arial"; + margin-top: 10vw; + margin-bottom: 10vw; + } + + body input[type=radio]:focus { + animation: shadowColorchange 13s infinite; + animation-direction: alternate; + } + + .downloader { + width: 100%; + margin-top: 3vw; + margin-bottom: 3vw; + } + + .downloadbtn, .downloadurl { + border: none; + margin: auto; + padding: 0.5em 0.5vw; + align-content: center; + font-size: 16px; + } + + .downloadurl { + width: 10em; + background-color: white; + color: black; + border-radius: 15px 0px 0px 15px; + } + + .downloadurl[type=text]:focus, textarea:focus { + animation: shadowColorchange 13s infinite; + animation-direction: alternate; + } + + .downloadbtn { + color: black; + border-radius: 0px 15px 15px 0px; + animation: colorchange 13s infinite; + animation-direction: alternate-reverse; + } + + a:link { + color: red; + } + + a:visited { + color: lightblue; + } + + @keyframes shadowColorchange { + 0% { + box-shadow: 0 0 15px #ff2400; + border: 0px solid #ff2400; + } + 10% { + box-shadow: 0 0 15px #e81d1d; + border: 0px solid #e81d1d; + } + 20% { + box-shadow: 0 0 15px #e8b71d; + border: 0px solid #e8b71d; + } + 30% { + box-shadow: 0 0 15px #e3e81d; + border: 0px solid #e3e81d; + } + 40% { + box-shadow: 0 0 15px #1de840; + border: 0px solid #1de840; + } + 50% { + box-shadow: 0 0 15px #1ddde8; + border: 0px solid #1ddde8; + } + 60% { + box-shadow: 0 0 15px #2b1de8; + border: 0px solid #2b1de8; + } + 70% { + box-shadow: 0 0 15px #dd00f3; + border: 0px solid #dd00f3; + } + 80% { + box-shadow: 0 0 15px #dd00f3; + border: 0px solid #dd00f3; + } + 90% { + box-shadow: 0 0 15px #ff2400; + border: 0px solid #ff2400; + } + 100% { + box-shadow: 0 0 15px #ff2400; + border: 0px solid #ff2400; + } + } + + + @keyframes colorchange { + 0% { + background-color: #ff2400; + color: white; + } + 10% { + background-color: #e81d1d; + } + 20% { + background-color: #e8b71d; + color: black; + } + 30% { + background-color: #e3e81d; + } + 40% { + background-color: #1de840; + color: white; + } + 50% { + background-color: #1ddde8; + color: black; + } + 60% { + background-color: #2b1de8; + } + 70% { + background-color: #dd00f3; + } + 80% { + background-color: #dd00f3; + } + 90% { + background-color: #ff2400; + } + 100% { + background-color: #ff2400; + } + } + + \ No newline at end of file diff --git a/public/thumbnail/thumbnail goes here b/public/thumbnail/thumbnail goes here new file mode 100644 index 0000000..e69de29 diff --git a/public/uploads/upload goes here b/public/uploads/upload goes here new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/index.edge b/resources/views/index.edge new file mode 100644 index 0000000..e0349e8 --- /dev/null +++ b/resources/views/index.edge @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + {{ title }} + + +
+
+
+ @if(announcement || (day == '24' || day == '25') && month == '11') +
+
+ Announcement + +
+
+ @if((day == '24' || day == '25') && month == '11') +

Happy Christmas!

+ @endif +

{{ announcement }}

+
+
+ @endif +
+
+
+ +
+
+
+

{{ title }}

+
+ {{ csrfField() }} + +
+
+
+
+ + + + + + + +
+
+
+
+ +
+
+
+

+ +

+

+ +

+
+
+
+ + +
+
+
+
+
+
+ +
+
+
+
+ + + + + +
+
+
+
+
+ +
+
+ @if(error) +
+ + {{ errormsg }} +
+ @endif +
+
+
+
+ + @if(file != "") +
+

Recently downloaded videos

+
+ @each(file in file) +
+
+

{{ file.name }}

+

{{ file.size }} MB

+
+
+

Download

+

+
+
+ +
+
+
+ @endeach +
+
+ @endif + + + @if(month == '11') + + @endif + + + \ No newline at end of file diff --git a/resources/views/legacy.edge b/resources/views/legacy.edge new file mode 100644 index 0000000..7ff41f2 --- /dev/null +++ b/resources/views/legacy.edge @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + {{ title }} (legacy) + + + @if(announcement || (day == '24' || day == '25') && month == '11') +
+ @if((day == '24' || day == '25') && month == '11') +

Happy christmas!

+ @endif +

{{ announcement }}

+

This part of the website is legacy which mean it can be broken at any time!

+
+ @endif +
+

{{ title }} (legacy)

+
+ {{ csrfField() }} + + + + + + + + + + + + + +
+
+ + + +
+
+ + + + + + + + + +

+
+ + @if(error) +

{{ errormsg }}

+ @endif + +
+ + + +@if(month == '11') + +@endif + + \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..add041a --- /dev/null +++ b/server.js @@ -0,0 +1,24 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Http server +|-------------------------------------------------------------------------- +| +| This file bootstrap Adonisjs to start the HTTP server. You are free to +| customize the process of booting the http server. +| +| """ Loading ace commands """ +| At times you may want to load ace commands when starting the HTTP server. +| Same can be done by chaining `loadCommands()` method after +| +| """ Preloading files """ +| Also you can preload files by calling `preLoad('path/to/file')` method. +| Make sure to pass relative path from the project root. +*/ + +const { Ignitor } = require('@adonisjs/ignitor') +new Ignitor(require('@adonisjs/fold')) + .appRoot(__dirname) + .fireHttpServer() + .catch(console.error) diff --git a/start/app.js b/start/app.js new file mode 100644 index 0000000..0f01b56 --- /dev/null +++ b/start/app.js @@ -0,0 +1,62 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Providers +|-------------------------------------------------------------------------- +| +| Providers are building blocks for your Adonis app. Anytime you install +| a new Adonis specific package, chances are you will register the +| provider here. +| +*/ +const providers = [ + '@adonisjs/framework/providers/AppProvider', + '@adonisjs/framework/providers/ViewProvider', + '@adonisjs/lucid/providers/LucidProvider', + '@adonisjs/bodyparser/providers/BodyParserProvider', + '@adonisjs/cors/providers/CorsProvider', + '@adonisjs/shield/providers/ShieldProvider', + '@adonisjs/session/providers/SessionProvider', + '@adonisjs/auth/providers/AuthProvider', + '@adonisjs/validator/providers/ValidatorProvider', +] + +/* +|-------------------------------------------------------------------------- +| Ace Providers +|-------------------------------------------------------------------------- +| +| Ace providers are required only when running ace commands. For example +| Providers for migrations, tests etc. +| +*/ +const aceProviders = [ + '@adonisjs/lucid/providers/MigrationsProvider' +] + +/* +|-------------------------------------------------------------------------- +| Aliases +|-------------------------------------------------------------------------- +| +| Aliases are short unique names for IoC container bindings. You are free +| to create your own aliases. +| +| For example: +| { Route: 'Adonis/Src/Route' } +| +*/ +const aliases = {} + +/* +|-------------------------------------------------------------------------- +| Commands +|-------------------------------------------------------------------------- +| +| Here you store ace commands for your package +| +*/ +const commands = [] + +module.exports = { providers, aceProviders, aliases, commands } diff --git a/start/kernel.js b/start/kernel.js new file mode 100644 index 0000000..f51093a --- /dev/null +++ b/start/kernel.js @@ -0,0 +1,62 @@ +'use strict' + +/** @type {import('@adonisjs/framework/src/Server')} */ +const Server = use('Server') + +/* +|-------------------------------------------------------------------------- +| Global Middleware +|-------------------------------------------------------------------------- +| +| Global middleware are executed on each http request only when the routes +| match. +| +*/ +const globalMiddleware = [ + 'Adonis/Middleware/BodyParser', + 'Adonis/Middleware/Session', + 'Adonis/Middleware/Shield', + 'Adonis/Middleware/AuthInit', +] + +/* +|-------------------------------------------------------------------------- +| Named Middleware +|-------------------------------------------------------------------------- +| +| Named middleware is key/value object to conditionally add middleware on +| specific routes or group of routes. +| +| // define +| { +| auth: 'Adonis/Middleware/Auth' +| } +| +| // use +| Route.get().middleware('auth') +| +*/ +const namedMiddleware = { + auth: 'Adonis/Middleware/Auth', + guest: 'Adonis/Middleware/AllowGuestOnly' +} + +/* +|-------------------------------------------------------------------------- +| Server Middleware +|-------------------------------------------------------------------------- +| +| Server level middleware are executed even when route for a given URL is +| not registered. Features like `static assets` and `cors` needs better +| control over request lifecycle. +| +*/ +const serverMiddleware = [ + 'Adonis/Middleware/Static', + 'Adonis/Middleware/Cors' +] + +Server + .registerGlobal(globalMiddleware) + .registerNamed(namedMiddleware) + .use(serverMiddleware) diff --git a/start/routes.js b/start/routes.js new file mode 100644 index 0000000..55efc2f --- /dev/null +++ b/start/routes.js @@ -0,0 +1,20 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Routes +|-------------------------------------------------------------------------- +| +| Http routes are entry points to your web application. You can create +| routes for different URL's and bind Controller actions to them. +| +| A complete guide on routing is available here. +| http://adonisjs.com/docs/4.1/routing +| +*/ + +/** @type {typeof import('@adonisjs/framework/src/Route/Manager')} */ +const Route = use('Route') + +Route.get('/:legacy?', 'DownloadController.index') +Route.post('/:legacy?', 'DownloadController.download') \ No newline at end of file