From 97c346e376e9dd3dc0acbeee680961f6dfb4c307 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Mon, 4 Mar 2024 20:30:27 +0000 Subject: [PATCH 1/2] fix: upgrade @mdx-js/react from 3.0.0 to 3.0.1 Snyk has created this PR to upgrade @mdx-js/react from 3.0.0 to 3.0.1. See this package in npm: https://www.npmjs.com/package/@mdx-js/react See this project in Snyk: https://app.snyk.io/org/jlenon7/project/66be1127-89ac-42d7-80fc-9dc608d6a917?utm_source=github&utm_medium=referral&page=upgrade-pr --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa708220..26b3afff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@fortawesome/free-regular-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", - "@mdx-js/react": "^3.0.0", + "@mdx-js/react": "^3.0.1", "clsx": "^2.1.0", "prism": "^4.1.2", "prism-react-renderer": "^2.2.0", @@ -3036,9 +3036,9 @@ } }, "node_modules/@mdx-js/react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", - "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", + "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", "dependencies": { "@types/mdx": "^2.0.0" }, diff --git a/package.json b/package.json index a6d0f252..1779ae73 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@fortawesome/free-regular-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", - "@mdx-js/react": "^3.0.0", + "@mdx-js/react": "^3.0.1", "clsx": "^2.1.0", "prism": "^4.1.2", "prism-react-renderer": "^2.2.0", From 2f9057dcc630e9b5785d3689def91d4cb78da9df Mon Sep 17 00:00:00 2001 From: jlenon7 Date: Wed, 6 Mar 2024 13:30:36 +0000 Subject: [PATCH 2/2] chore(npm): update dependencies --- docs/database/getting-started.mdx | 18 +- docs/digging-deeper/mail.mdx | 157 +++++++++++++++++- docs/getting-started/directory-structure.mdx | 4 + docs/rest-api-application/annotations.mdx | 14 +- docs/rest-api-application/controllers.mdx | 4 +- docs/rest-api-application/middlewares.mdx | 18 +- docs/rest-api-application/request-context.mdx | 15 +- docs/rest-api-application/routing.mdx | 37 ++++- docs/the-basics/helpers.mdx | 40 +++++ 9 files changed, 260 insertions(+), 47 deletions(-) diff --git a/docs/database/getting-started.mdx b/docs/database/getting-started.mdx index 1e335099..551eca33 100644 --- a/docs/database/getting-started.mdx +++ b/docs/database/getting-started.mdx @@ -17,17 +17,13 @@ using a fluent query builder and the ORM. ## Installation -First of all you need to install `@athenna/database` package: +First of all you need to install `@athenna/database` package +and configure it. Artisan provides a very simple command to +install and configure the database library in your project. +Simply run the following: ```bash -npm i @athenna/database -``` - -Artisan provides a very simple command to configure the database -library in your project. Simply run the following: - -```bash -node artisan configure @athenna/database +node artisan install @athenna/database ``` The database configurer will do the following operations in @@ -39,7 +35,9 @@ your project: - Add all database commands in your `.athennarc.json` file. - Add all database template files in your `.athennarc.json` file. - Add database environment variables to `.env`, `.env.test` and `.env.example`. -- Configure the `docker-compose.yml` file acordding to the database selected. +- Configure the `docker-compose.yml` file according to the database selected. +- Install libraries like `knex` and `pg`. It will always depends on the default +database you selected. ## Configuration diff --git a/docs/digging-deeper/mail.mdx b/docs/digging-deeper/mail.mdx index ea046c63..e66b0875 100644 --- a/docs/digging-deeper/mail.mdx +++ b/docs/digging-deeper/mail.mdx @@ -10,4 +10,159 @@ See how to send emails in Athenna. ## Introduction -Coming soon +Sending email doesn't have to be complicated. Athenna provides a +clean, simple email API powered by the popular [nodemailer] package. +Right now Athenna provide drivers for sending email via SMTP, only +but in the future we will add support for Mailgun, Mailtrap, +Amazon SES, and sendmail. + +## Installation + +First of all you need to install `@athenna/mail` package +and configure it. Artisan provides a very simple command to +install and configure the mail library in your project. +Simply run the following: + +```bash +node artisan install @athenna/mail +``` + +The mail configurer will do the following operations in +your project: + +- Create the `mail.ts` configuration file. +- Add all mail providers in your `.athennarc.json` file. +- Add mail environment variables to `.env`, `.env.test` and `.env.example`. + +## Configuration + +Athenna's email services may be configured via your application's +`Path.config('mail.ts')` configuration file. Each mailer configured +within this file may have its own unique configuration and even +its own unique "transport", allowing your application to use different +email services to send certain email messages. + +### Available mail drivers + +Each mailer is powered by a "driver". The driver determines how +the mail will be transported. The following mail drivers are +available in every Athenna application. An entry for most of +these drivers is already present in your application's +`Path.config('mail.ts')` configuration file, so be sure to +review this file to become familiar with its contents: + +| Driver name | Website | Built with | +|:------------|:----------------------------:|------------------------------------------------------:| +| `smtp` | https://nodemailer.com/smtp/ |[nodemailer](https://www.npmjs.com/package/nodemailer) | + +:::note + +Athenna has another driver called `fake` that is very helpful when running tests. +The `fake` driver got the same signature of all other drivers, but it don't execute +any operation when calling executors methods like `send()`, which is perfect to use +within the [`Mock`](/docs/testing/mocking) class. For more information +about the `FakeDriver`, take a look at the +[mocking mail documentation section.](/docs/testing/mocking#mocking-mail) + +::: + +## Sending mail + +Let's see the simplest way to send a mail using Mail facade: + +```typescript +import { Mail } from '@athenna/mail' + +await Mail.from('support@athenna.io') + .to('user@gmail.com') + .text('Mail content') + .send() +``` + +You can add "to", "cc" and "bcc" recipientes by chaining +their respective method together: + +```typescript +await Mail.from('support@athenna.io') + .to('user@gmail.com') + .cc('txsoura@athenna.io') + .bcc('support@athenna.io') + .text('Mail content') + .send() +``` + +### Sending mail via a specific mailer + +By default, Athenna will send email using the mailer configured +as the `default` mailer in your application's `mail` configuration +file. However, you may use the `mailer()` method to send a message +using a specific mailer configuration: + +```typescript +await Mail.mailer('my-mailer') + .from('support@athenna.io') + .to('lenon@athenna.io') + .text('Mail content') + .send() +``` + +### Sending HTML and Markdown as content + +To send HTML as the content of the mail, you can use the `html()` +method: + +```typescript +await Mail.mailer('my-mailer') + .from('support@athenna.io') + .to('lenon@athenna.io') + .html('

Mail content

') + .send() +``` + +And for markdowns you can use the `markdown()` method: + +```typescript +await Mail.mailer('my-mailer') + .from('support@athenna.io') + .to('lenon@athenna.io') + .markdown('# Mail content') + .send() +``` + +### Using view template as content + +The mail component is integrated with the view component to be +able to render and send a view as the body of the email. To do +so you can use the `view()` method of the Mail facade: + +```typescript +const userEmail = 'lenon@athenna.io' + +await Mail.from('support@athenna.io') + .to(userEmail) + .view('mail/welcome', { email: userEmail }) + .send() +``` + +### Previewing mail templates in the browser + +When designing a mailable's template, it is convenient to quickly +preview the rendered mailable in your browser like a typical +Edge template. For this reason, Athenna allows you to return any +mailable directly from a route closure or controller by using the +`response.view()` method, allowing you to quickly preview its +design without needing to send it to an actual email address: + +```typescript title="Path.routes('http.ts')" +Route.get('/mailable', ({ response }) => { + return response.view('mail.welcome', { email: 'lenon@athenna.io' }) +}) +``` + + + + + + + + diff --git a/docs/getting-started/directory-structure.mdx b/docs/getting-started/directory-structure.mdx index 19d37bb3..34860bf6 100644 --- a/docs/getting-started/directory-structure.mdx +++ b/docs/getting-started/directory-structure.mdx @@ -47,6 +47,10 @@ This directory does not exist by default, but will be created for you if you execute the following command: ```bash +# To install the library and configure it +node artisan install @athenna/database + +# To just configure the library node artisan configure @athenna/database ``` diff --git a/docs/rest-api-application/annotations.mdx b/docs/rest-api-application/annotations.mdx index b0824f9d..4343b02b 100644 --- a/docs/rest-api-application/annotations.mdx +++ b/docs/rest-api-application/annotations.mdx @@ -19,8 +19,8 @@ import { Controller, type Context } from '@athenna/http' @Controller() export class UserController { - public async index(ctx: Context) { - return ctx.response.status(200).send({ status: 'ok' }) + public async index(({ response }): Context) { + return response.status(200).send({ status: 'ok' }) } } ``` @@ -80,8 +80,8 @@ import { Middleware, type Context } from '@athenna/http' @Middleware() export class UserMiddleware { - public async handle(ctx: Context) { - ctx.user = await User.find({ id: ctx.params.id }) + public async handle(({ request, data }): Context) { + data.user = await User.find({ id: request.param('id') }) } } ``` @@ -164,10 +164,10 @@ import { Interceptor, type Context } from '@athenna/http' @Interceptor() export class UserInterceptor { - public async intercept(ctx: Context) { - ctx.body.intercepted = true + public async intercept(({ response }): Context) { + response.body.intercepted = true - return ctx.body + return response.body } } ``` diff --git a/docs/rest-api-application/controllers.mdx b/docs/rest-api-application/controllers.mdx index 1f49cf17..f1a8902e 100644 --- a/docs/rest-api-application/controllers.mdx +++ b/docs/rest-api-application/controllers.mdx @@ -29,8 +29,8 @@ import { Context, Controller } from '@athenna/http' @Controller() export class UserController { - public async show({ response, params }: Context) { - return response.send(`User_${params.id}`) + public async show({ request, response }: Context) { + return response.send(`User_${request.param('id')}`) } } ``` diff --git a/docs/rest-api-application/middlewares.mdx b/docs/rest-api-application/middlewares.mdx index 10b86e71..0d28da87 100644 --- a/docs/rest-api-application/middlewares.mdx +++ b/docs/rest-api-application/middlewares.mdx @@ -45,8 +45,8 @@ import { Context, Middleware, MiddlewareContract } from '@athenna/http' @Middleware() export class EnsureApiKeyIsValid implements MiddlewareContract { - public async handle(ctx: Context): Promise { - if (queries.apiKey !== 'my-api-key') { + public async handle(({ request }): Context): Promise { + if (request.query('apiKey') !== 'my-api-key') { throw new UnauthorizedException('Your api key does not match with application api key.') } } @@ -108,8 +108,8 @@ import type { Context, MiddlewareContract } from '@athenna/http' @Middleware({ isGlobal: true }) 👈 export class EnsureApiKeyIsValid implements MiddlewareContract { - public async handle(ctx: Context): Promise { - if (queries.apiKey !== 'my-api-key') { + public async handle(({ request }): Context): Promise { + if (request.query('apiKey') !== 'my-api-key') { throw new UnauthorizedException('Your api key does not match with application api key.') } } @@ -141,8 +141,8 @@ import type { Context, MiddlewareContract } from '@athenna/http' @Middleware({ name: 'auth' }) 👈 export class EnsureApiKeyIsValid implements MiddlewareContract { - public async handle(ctx: Context): Promise { - if (queries.apiKey !== 'my-api-key') { + public async handle(({ request }): Context): Promise { + if (request.query('apiKey') !== 'my-api-key') { throw new UnauthorizedException('Your api key does not match with application api key.') } } @@ -198,10 +198,10 @@ import type { InterceptContext, InterceptorContract } from '@athenna/http' @Interceptor() export class InterceptMiddleware implements InterceptorContract { - public async intercept(ctx: InterceptContext): Promise { - ctx.body.intercepted = true + public async intercept(({ response }): InterceptContext): Promise { + response.body.intercepted = true - return ctx.body + return response.body } } ``` diff --git a/docs/rest-api-application/request-context.mdx b/docs/rest-api-application/request-context.mdx index 0734cb0f..c44afdca 100644 --- a/docs/rest-api-application/request-context.mdx +++ b/docs/rest-api-application/request-context.mdx @@ -400,15 +400,7 @@ Route.get('/welcome', ({ response }) => { }) ``` -### The params object -The `params` object contains the actual HTTP params of the -request that is being handled by your application. - -### The queries object - -The `queries` object contains the actual HTTP queries of the -request that is being handled by your application. ### The data object @@ -459,12 +451,11 @@ interface from `@athenna/http` package. In [interceptors](/docs/rest-api-application/middlewares#intercept-middleware) Athenna uses the `InterceptContext`. This context is quite -the same of `Context`, but it has additional properties -`body` and `status`. +the same of `Context`, but it has additional property `status`. ### Terminate middleware context In [terminators](/docs/rest-api-application/middlewares#terminate-middleware) Athenna set the `TerminateContext`. This context is quite -the same of `Context`, but it has additional properties -`body`, `status` and `responseTime`. +the same of `Context`, but it has additional properties +`status` and `responseTime`. diff --git a/docs/rest-api-application/routing.mdx b/docs/rest-api-application/routing.mdx index 8c526758..5042a73d 100644 --- a/docs/rest-api-application/routing.mdx +++ b/docs/rest-api-application/routing.mdx @@ -58,10 +58,35 @@ incoming request is matched with the correct route. ::: +## View routes + +You may use the `Route.view()` method if you have a route handler +that only renders a view. It is a convenient shortcut to render +a view without defining an explicit handler: + +```typescript +Route.view('/welcome', 'welcome') +``` + +The example above is just a shortcut for the following: + +```typescript +Route.get('/welcome', ({ response }) => { + return response.view('welcome') +}) +``` + +Optionally, you can pass the template data as the third argument, +just like in `response.view()` method: + +```typescript +Route.view('/welcome', 'welcome', { name: 'lenon' }) +``` + ## Redirect routes If you are defining a route that redirects to another -`url`, you may use the `Route.redirect` method. This +`url`, you may use the `Route.redirect()` method. This method provides a convenient shortcut so that you do not have to define a full route or controller for performing a simple redirect: @@ -70,9 +95,9 @@ a simple redirect: Route.redirect('/here', '/there') ``` -By default, `Route.redirect` returns a `302` status code. -You may customize the status code using the optional -third parameter: +By default, `Route.redirect()` method returns a `302` +status code. You may customize the status code using +the optional third parameter: ```typescript Route.redirect('/here', '/there', 301) @@ -104,8 +129,8 @@ a user's ID from the URL. You may do so by defining route parameters: ```typescript -Route.get('/user/:id', ({ response, params }) => { - return response.send({ user: `User ${params.id}` }) +Route.get('/user/:id', ({ request, response }) => { + return response.send({ user: `User ${request.param('id')}` }) }) ``` diff --git a/docs/the-basics/helpers.mdx b/docs/the-basics/helpers.mdx index d5b8bae5..91d2871a 100644 --- a/docs/the-basics/helpers.mdx +++ b/docs/the-basics/helpers.mdx @@ -294,6 +294,46 @@ const newArray = await Exec.concurrently(array, async (number) => { }) ``` +### `Exec::link()` + +Link some library using any kind of package manager +(tested only with `npm`): + +```typescript +import { Exec } from '@athenna/common' + +await Exec.link('@athenna/mail') +await Exec.link(['@athenna/mail', '@athenna/database'], { registry: 'npm' }) +await Exec.link(['@athenna/mail', '@athenna/database'], { + silent: true, + args: ['-D'], + reject: false, + registry: 'npm', + cwd: Path.pwd('other-pkg'), +}) +``` + +### `Exec::install()` + +Install some library using any kind of package manager +(tested only with `npm`): + +```typescript +import { Exec } from '@athenna/common' + +await Exec.install('@athenna/mail') +await Exec.install(['@athenna/mail', '@athenna/database'], { registry: 'npm' }) +await Exec.install(['@athenna/mail', '@athenna/database'], { + silent: true, + dev: false, + args: ['-D'], + cached: false, + reject: false, + registry: 'npm', + cwd: Path.pwd('other-pkg'), +}) +``` + #### `Exec::command()` Execute some command of your OS in a child process: