Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
alexgavrusev committed Jan 28, 2024
0 parents commit 9008bc3
Show file tree
Hide file tree
Showing 47 changed files with 11,162 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .commitlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {}
}
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Editor configuration, see http://editorconfig.org
root = true

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

[*.md]
max_line_length = off
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
30 changes: 30 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"root": true,
"ignorePatterns": ["!**/*"],
"plugins": ["@nx"],
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nx/typescript"],
"rules": {}
},
{
"files": ["*.js", "*.jsx", "*.mjs"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": [
"error",
{
"ignoredFiles": ["{projectRoot}/vite.config.{js,ts,mjs,mts}"],
"ignoredDependencies": ["@swc/helpers"]
}
]
}
}
]
}
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI
on:
push:
branches:
- main
pull_request:

jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint, test, e2e, build
run: pnpm exec nx run-many -t lint,test,e2e,build
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
dist
tmp
/out-tsc

# dependencies
node_modules

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
.DS_Store
Thumbs.db

.nx/cache
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

pnpm commitlint --edit "$1"
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
strict-peer-dependencies=false
auto-install-peers=true
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage
/.nx/cache
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"singleQuote": true
}
29 changes: 29 additions & 0 deletions .swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"jsc": {
"target": "es2017",
"parser": {
"syntax": "typescript",
"decorators": true,
"dynamicImport": true
},
"transform": {
"decoratorMetadata": true,
"legacyDecorator": true
},
"keepClassNames": true,
"externalHelpers": true,
"loose": true
},
"module": {
"type": "es6"
},
"sourceMaps": true,
"exclude": [
"jest.config.ts",
".*\\.spec.tsx?$",
".*\\.test.tsx?$",
"./src/jest-setup.ts$",
"./**/jest-setup.ts$",
".*.js$"
]
}
28 changes: 28 additions & 0 deletions .verdaccio/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# path to a directory with all packages
storage: ../tmp/local-registry/storage

# a list of other known repositories we can talk to
uplinks:
npmjs:
url: https://registry.npmjs.org/
maxage: 60m

packages:
'**':
# give all users (including non-authenticated users) full access
# because it is a local registry
access: $all
publish: $all
unpublish: $all

# if package is not available locally, proxy requests to npm registry
proxy: npmjs

# log settings
logs:
type: stdout
format: pretty
level: warn

publish:
allow_offline: true # set offline to true to allow publish offline
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"nrwl.angular-console",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
]
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Aliaksandr Haurusiou

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
133 changes: 133 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# @gvrs/nestjs-hcaptcha

A NestJS module for adding hCaptcha validation

## Installation

```bash
npm i @gvrs/nestjs-hcaptcha
```

## Usage

First, provide the options in the root `AppModule`:

```ts
@Module({
imports: [
HcaptchaModule.forRoot({
secret: 'YOUR_HCAPTCHA_SECRET',
}),
],
})
export class AppModule {}
```

Afterward, import the `HcaptchaModule` in the module where you need to use captcha validation:

```ts
@Module({
imports: [HcaptchaModule],
controllers: [UsersController],
})
export class UsersModule {}
```

Finally, decorate the controller method with `@VerifyCaptcha()`:

```ts
@Controller('users')
class UsersController {
@Post('register')
@VerifyCaptcha()
register() {}
}
```

By default, the hCaptcha token will be extracted from the `h-captcha-response` request body field

### Setting the sitekey you expect to see

Provide the `sitekey` option in the root `HcaptchaModule`:

```ts
@Module({
imports: [
HcaptchaModule.forRoot({
secret: 'YOUR_HCAPTCHA_SECRET',
sitekey: 'YOUR_SITEKEY',
}),
],
})
export class AppModule {}
```

### Customizing the captcha data extraction

If you want to customize the retrieval of the hCaptcha token and/or the user's IP address, provide an implementation of `getCaptchaData` in the root `HcaptchaModule`:

```ts
@Module({
imports: [
HcaptchaModule.forRoot({
secret: 'YOUR_HCAPTCHA_SECRET',
getCaptchaData: (ctx) => {
const request = ctx.switchToHttp().getRequest();

const token = request.body['token'];
const remoteip = request.headers['x-forwarded-for'];

return { token, remoteip };
},
}),
],
})
export class AppModule {}
```

### Customizing the error response

By default, when the captcha is invalid, or cannot be validated, a 403 error will be sent to the client. To customize that response, use an [exception filter](https://docs.nestjs.com/exception-filters):

```ts
@Catch(HcaptchaException)
class HcaptchaExceptionFilter implements ExceptionFilter {
catch(exception: HcaptchaException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = 400;

response.status(status).json({
statusCode: status,
message: 'Invalid captcha',
});
}
}
```

### Imperative captcha verification

If you don't want to, or cannot use the `@VerifyCaptha()` decorator or the `HcaptchaGuard`, you can verify the captcha by using the `HcaptchaService`:

```ts
@Controller('users')
class UsersController {
constructor(private readonly hcaptchaService: HcaptchaService) {}

@Post('register')
async register(@Req() request: Request) {
try {
const token = request.body['h-captcha-response'];

// returns the hCaptcha JSON response, or throws a HcaptchaException
const verifyResponse = await this.hcaptchaService.verifyCaptcha(token);
} catch {
throw new BadRequestException();
}
}
}
```

## License

MIT © Aliaksandr Haurusiou.
Loading

0 comments on commit 9008bc3

Please sign in to comment.