Compare commits
No commits in common. "discours" and "feat/2fa" have entirely different histories.
|
@ -2,4 +2,3 @@ ENV=production
|
||||||
DATABASE_URL=data.db
|
DATABASE_URL=data.db
|
||||||
DATABASE_TYPE=sqlite
|
DATABASE_TYPE=sqlite
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
||||||
DISABLE_PLAYGROUND=true
|
|
||||||
|
|
|
@ -7,9 +7,3 @@ SMTP_PORT=2525
|
||||||
SMTP_USERNAME=test
|
SMTP_USERNAME=test
|
||||||
SMTP_PASSWORD=test
|
SMTP_PASSWORD=test
|
||||||
SENDER_EMAIL="info@authorizer.dev"
|
SENDER_EMAIL="info@authorizer.dev"
|
||||||
TWILIO_API_KEY=test
|
|
||||||
TWILIO_API_SECRET=test
|
|
||||||
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
||||||
TWILIO_SENDER=909921212112
|
|
||||||
SENDER_NAME="Authorizer"
|
|
||||||
AWS_REGION=ap-south-1
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
name: "deploy"
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Cloning repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Get Repo Name
|
|
||||||
id: repo_name
|
|
||||||
run: echo "::set-output name=repo::$(echo ${GITHUB_REPOSITORY##*/})"
|
|
||||||
|
|
||||||
- name: Get Branch Name
|
|
||||||
id: branch_name
|
|
||||||
run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})"
|
|
||||||
|
|
||||||
- name: Push branch 'discours-dev' to staging
|
|
||||||
if: steps.branch_name.outputs.branch == 'discours-dev'
|
|
||||||
uses: dokku/github-action@master
|
|
||||||
with:
|
|
||||||
branch: "main"
|
|
||||||
git_remote_url: "ssh://dokku@staging.discours.io:22/authorizer"
|
|
||||||
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Push branch 'discours' to v2.discours.io
|
|
||||||
if: steps.branch_name.outputs.branch == 'discours'
|
|
||||||
uses: dokku/github-action@master
|
|
||||||
with:
|
|
||||||
branch: "main"
|
|
||||||
git_remote_url: "ssh://dokku@v2.discours.io:22/authorizer"
|
|
||||||
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
git_push_flags: '--force'
|
|
230
.github/CONTRIBUTING.md
vendored
230
.github/CONTRIBUTING.md
vendored
|
@ -45,30 +45,12 @@ Please ask as many questions as you need, either directly in the issue or on [Di
|
||||||
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
|
||||||
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
|
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
|
||||||
3. Change directory to authorizer: `cd authorizer`
|
3. Change directory to authorizer: `cd authorizer`
|
||||||
4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
5. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
|
||||||
5. Build Dashboard `make build-dashboard`
|
6. Build Dashboard `make build-dashboard`
|
||||||
6. Build App `make build-app`
|
7. Build App `make build-app`
|
||||||
7. Build Server `make clean && make`
|
8. Build Server `make clean && make`
|
||||||
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
|
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
|
||||||
8. Run binary `./build/server`
|
9. Run binary `./build/server`
|
||||||
|
|
||||||
### Updating GraphQL schema
|
|
||||||
|
|
||||||
- Modify `server/graph/schema.graphqls` file
|
|
||||||
- Run `make generate-graphql` this will update the models and required methods
|
|
||||||
- If a new mutation or query is added
|
|
||||||
- Write the implementation for the new resolver in `server/resolvers/NEW_RESOLVER.GO`
|
|
||||||
- Update `server/graph/schema.resolvers.go` with the new resolver method
|
|
||||||
|
|
||||||
### Adding support for new database
|
|
||||||
|
|
||||||
- Run `make generate-db-template dbname=NEW_DB_NAME`
|
|
||||||
eg `make generate-db-template dbname=dynamodb`
|
|
||||||
|
|
||||||
This command will generate a folder in server/db/providers/ with name specified in the above command.
|
|
||||||
One will have to implement methods present in that folder.
|
|
||||||
|
|
||||||
> Note: Connection for database and schema changes are written in `server/db/providers/DB_NAME/provider.go` > `NewProvider` method is called for any given db based on the env variables present.
|
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
|
@ -105,145 +87,145 @@ For manually testing using graphql playground, you can paste following queries a
|
||||||
|
|
||||||
```gql
|
```gql
|
||||||
mutation Signup {
|
mutation Signup {
|
||||||
signup(
|
signup(
|
||||||
params: {
|
params: {
|
||||||
email: "lakhan@yopmail.com"
|
email: "lakhan@yopmail.com"
|
||||||
password: "test"
|
password: "test"
|
||||||
confirm_password: "test"
|
confirm_password: "test"
|
||||||
given_name: "lakhan"
|
given_name: "lakhan"
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
message
|
message
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
family_name
|
family_name
|
||||||
given_name
|
given_name
|
||||||
email
|
email
|
||||||
email_verified
|
email_verified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation ResendEamil {
|
mutation ResendEamil {
|
||||||
resend_verify_email(
|
resend_verify_email(
|
||||||
params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" }
|
params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" }
|
||||||
) {
|
) {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetVerifyRequests {
|
query GetVerifyRequests {
|
||||||
_verification_requests {
|
_verification_requests {
|
||||||
id
|
id
|
||||||
token
|
token
|
||||||
expires
|
expires
|
||||||
identifier
|
identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation VerifyEmail {
|
mutation VerifyEmail {
|
||||||
verify_email(params: { token: "" }) {
|
verify_email(params: { token: "" }) {
|
||||||
access_token
|
access_token
|
||||||
expires_at
|
expires_at
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
email
|
email
|
||||||
given_name
|
given_name
|
||||||
email_verified
|
email_verified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation Login {
|
mutation Login {
|
||||||
login(params: { email: "lakhan@yopmail.com", password: "test" }) {
|
login(params: { email: "lakhan@yopmail.com", password: "test" }) {
|
||||||
access_token
|
access_token
|
||||||
expires_at
|
expires_at
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
family_name
|
family_name
|
||||||
given_name
|
given_name
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetSession {
|
query GetSession {
|
||||||
session {
|
session {
|
||||||
access_token
|
access_token
|
||||||
expires_at
|
expires_at
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
given_name
|
given_name
|
||||||
family_name
|
family_name
|
||||||
email
|
email
|
||||||
email_verified
|
email_verified
|
||||||
signup_methods
|
signup_methods
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation ForgotPassword {
|
mutation ForgotPassword {
|
||||||
forgot_password(params: { email: "lakhan@yopmail.com" }) {
|
forgot_password(params: { email: "lakhan@yopmail.com" }) {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation ResetPassword {
|
mutation ResetPassword {
|
||||||
reset_password(
|
reset_password(
|
||||||
params: { token: "", password: "test", confirm_password: "test" }
|
params: { token: "", password: "test", confirm_password: "test" }
|
||||||
) {
|
) {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation UpdateProfile {
|
mutation UpdateProfile {
|
||||||
update_profile(params: { family_name: "samani" }) {
|
update_profile(params: { family_name: "samani" }) {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetUsers {
|
query GetUsers {
|
||||||
_users {
|
_users {
|
||||||
id
|
id
|
||||||
email
|
email
|
||||||
email_verified
|
email_verified
|
||||||
given_name
|
given_name
|
||||||
family_name
|
family_name
|
||||||
picture
|
picture
|
||||||
signup_methods
|
signup_methods
|
||||||
phone_number
|
phone_number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation MagicLinkLogin {
|
mutation MagicLinkLogin {
|
||||||
magic_link_login(params: { email: "test@yopmail.com" }) {
|
magic_link_login(params: { email: "test@yopmail.com" }) {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation Logout {
|
mutation Logout {
|
||||||
logout {
|
logout {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation UpdateUser {
|
mutation UpdateUser {
|
||||||
_update_user(
|
_update_user(
|
||||||
params: {
|
params: {
|
||||||
id: "dafc9400-d603-4ade-997c-83fcd54bbd67"
|
id: "dafc9400-d603-4ade-997c-83fcd54bbd67"
|
||||||
roles: ["user", "admin"]
|
roles: ["user", "admin"]
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
email
|
email
|
||||||
roles
|
roles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation DeleteUser {
|
mutation DeleteUser {
|
||||||
_delete_user(params: { email: "signup.test134523@yopmail.com" }) {
|
_delete_user(params: { email: "signup.test134523@yopmail.com" }) {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
@ -1,3 +0,0 @@
|
||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: authorizerdev
|
|
51
.github/workflows/release.yaml
vendored
51
.github/workflows/release.yaml
vendored
|
@ -7,9 +7,9 @@ on:
|
||||||
default: 'warning'
|
default: 'warning'
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- info
|
- info
|
||||||
- warning
|
- warning
|
||||||
- debug
|
- debug
|
||||||
tags:
|
tags:
|
||||||
description: 'Tags'
|
description: 'Tags'
|
||||||
required: false
|
required: false
|
||||||
|
@ -19,27 +19,19 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
releases:
|
releases:
|
||||||
name: Release Authorizer
|
name: Release Authorizer Binary
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '16'
|
||||||
- # Add support for more platforms with QEMU (optional)
|
|
||||||
# https://github.com/docker/setup-qemu-action
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
with:
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '^1.19.1'
|
go-version: '^1.17.3'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install build-essential wget zip libc6-dev-arm64-cross && \
|
sudo apt-get install build-essential wget zip gcc-mingw-w64 && \
|
||||||
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \
|
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \
|
||||||
wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \
|
wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \
|
||||||
tar -zxf github-assets-uploader.tar.gz && \
|
tar -zxf github-assets-uploader.tar.gz && \
|
||||||
|
@ -52,28 +44,25 @@ jobs:
|
||||||
run: whereis go
|
run: whereis go
|
||||||
- name: Print Go Version
|
- name: Print Go Version
|
||||||
run: go version
|
run: go version
|
||||||
- name: Install gox
|
|
||||||
run: go install github.com/mitchellh/gox@latest
|
|
||||||
- name: Set VERSION env
|
- name: Set VERSION env
|
||||||
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
|
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
|
||||||
- name: Copy .env file
|
- name: Copy .env file
|
||||||
run: mv .env.sample .env
|
run: mv .env.sample .env
|
||||||
- name: Build package
|
- name: Package files for windows
|
||||||
run: |
|
run: |
|
||||||
make clean && \
|
make clean && \
|
||||||
make build && \
|
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
|
||||||
mkdir -p authorizer-${VERSION}-darwin-arm64/build authorizer-${VERSION}-darwin-arm64/app authorizer-${VERSION}-darwin-arm64/dashboard && cp build/darwin/arm64/server authorizer-${VERSION}-darwin-arm64/build/ && cp .env authorizer-${VERSION}-darwin-arm64/.env && cp -rf app/build authorizer-${VERSION}-darwin-arm64/app/build && cp -rf templates authorizer-${VERSION}-darwin-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-arm64.tar.gz authorizer-${VERSION}-darwin-arm64 && \
|
mv build/server build/server.exe && \
|
||||||
mkdir -p authorizer-${VERSION}-darwin-amd64/build authorizer-${VERSION}-darwin-amd64/app authorizer-${VERSION}-darwin-amd64/dashboard && cp build/darwin/amd64/server authorizer-${VERSION}-darwin-amd64/build/ && cp .env authorizer-${VERSION}-darwin-amd64/.env && cp -rf app/build authorizer-${VERSION}-darwin-amd64/app/build && cp -rf templates authorizer-${VERSION}-darwin-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-amd64.tar.gz authorizer-${VERSION}-darwin-amd64 && \
|
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates dashboard/build
|
||||||
mkdir -p authorizer-${VERSION}-linux-amd64/build authorizer-${VERSION}-linux-amd64/app authorizer-${VERSION}-linux-amd64/dashboard && cp build/linux/amd64/server authorizer-${VERSION}-linux-amd64/build/ && cp .env authorizer-${VERSION}-linux-amd64/.env && cp -rf app/build authorizer-${VERSION}-linux-amd64/app/build && cp -rf templates authorizer-${VERSION}-linux-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz authorizer-${VERSION}-linux-amd64 && \
|
- name: Package files for linux
|
||||||
mkdir -p authorizer-${VERSION}-linux-arm64/build authorizer-${VERSION}-linux-arm64/app authorizer-${VERSION}-linux-arm64/dashboard && cp build/linux/arm64/server authorizer-${VERSION}-linux-arm64/build/ && cp .env authorizer-${VERSION}-linux-arm64/.env && cp -rf app/build authorizer-${VERSION}-linux-arm64/app/build && cp -rf templates authorizer-${VERSION}-linux-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-arm64.tar.gz authorizer-${VERSION}-linux-arm64 && \
|
run: |
|
||||||
mkdir -p authorizer-${VERSION}-windows-amd64/build authorizer-${VERSION}-windows-amd64/app authorizer-${VERSION}-windows-amd64/dashboard && cp build/windows/amd64/server.exe authorizer-${VERSION}-windows-amd64/build/ && cp .env authorizer-${VERSION}-windows-amd64/.env && cp -rf app/build authorizer-${VERSION}-windows-amd64/app/build && cp -rf templates authorizer-${VERSION}-windows-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-windows-amd64/dashboard/build && zip -vr authorizer-${VERSION}-windows-amd64.zip authorizer-${VERSION}-windows-amd64
|
make clean && \
|
||||||
|
CGO_ENABLED=1 make && \
|
||||||
|
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates dashboard/build
|
||||||
- name: Upload assets
|
- name: Upload assets
|
||||||
run: |
|
run: |
|
||||||
github-assets-uploader -f authorizer-${VERSION}-darwin-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \
|
||||||
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
|
||||||
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||||
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
|
||||||
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
with:
|
with:
|
||||||
|
@ -85,11 +74,6 @@ jobs:
|
||||||
uses: docker/metadata-action@v3
|
uses: docker/metadata-action@v3
|
||||||
with:
|
with:
|
||||||
images: lakhansamani/authorizer
|
images: lakhansamani/authorizer
|
||||||
tags: |
|
|
||||||
type=schedule
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=ref,event=pr
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
|
@ -98,6 +82,5 @@ jobs:
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
build-args: |
|
build-args: |
|
||||||
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -16,7 +16,3 @@ test.db
|
||||||
.yalc
|
.yalc
|
||||||
yalc.lock
|
yalc.lock
|
||||||
certs/
|
certs/
|
||||||
*-shm
|
|
||||||
*-wal
|
|
||||||
.idea
|
|
||||||
*.iml
|
|
||||||
|
|
20
Dockerfile
20
Dockerfile
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21.3-alpine3.18 AS go-builder
|
FROM golang:1.17-alpine as go-builder
|
||||||
WORKDIR /authorizer
|
WORKDIR /authorizer
|
||||||
COPY server server
|
COPY server server
|
||||||
COPY Makefile .
|
COPY Makefile .
|
||||||
|
@ -11,7 +11,7 @@ RUN apk add build-base &&\
|
||||||
make clean && make && \
|
make clean && make && \
|
||||||
chmod 777 build/server
|
chmod 777 build/server
|
||||||
|
|
||||||
FROM node:20-alpine3.18 AS node-builder
|
FROM node:17-alpine3.12 as node-builder
|
||||||
WORKDIR /authorizer
|
WORKDIR /authorizer
|
||||||
COPY app app
|
COPY app app
|
||||||
COPY dashboard dashboard
|
COPY dashboard dashboard
|
||||||
|
@ -20,16 +20,14 @@ RUN apk add build-base &&\
|
||||||
make build-app && \
|
make build-app && \
|
||||||
make build-dashboard
|
make build-dashboard
|
||||||
|
|
||||||
FROM alpine:3.18
|
FROM alpine:latest
|
||||||
RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
|
WORKDIR /root/
|
||||||
WORKDIR /authorizer
|
|
||||||
RUN mkdir app dashboard
|
RUN mkdir app dashboard
|
||||||
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/build app/build
|
COPY --from=node-builder /authorizer/app/build app/build
|
||||||
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/favicon_io app/favicon_io
|
COPY --from=node-builder /authorizer/app/favicon_io app/favicon_io
|
||||||
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/build dashboard/build
|
COPY --from=node-builder /authorizer/dashboard/build dashboard/build
|
||||||
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/favicon_io dashboard/favicon_io
|
COPY --from=node-builder /authorizer/dashboard/favicon_io dashboard/favicon_io
|
||||||
COPY --from=go-builder --chown=nobody:nobody /authorizer/build build
|
COPY --from=go-builder /authorizer/build build
|
||||||
COPY templates templates
|
COPY templates templates
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
USER authorizer
|
|
||||||
CMD [ "./build/server" ]
|
CMD [ "./build/server" ]
|
||||||
|
|
38
Makefile
38
Makefile
|
@ -3,12 +3,6 @@ VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
|
||||||
|
|
||||||
cmd:
|
cmd:
|
||||||
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
|
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
|
||||||
build:
|
|
||||||
cd server && gox \
|
|
||||||
-osarch="linux/amd64 linux/arm64 darwin/arm64 darwin/amd64 windows/amd64" \
|
|
||||||
-ldflags "-w -X main.VERSION=$(VERSION)" \
|
|
||||||
-output="../build/{{.OS}}/{{.Arch}}/server" \
|
|
||||||
./...
|
|
||||||
build-app:
|
build-app:
|
||||||
cd app && npm i && npm run build
|
cd app && npm i && npm run build
|
||||||
build-dashboard:
|
build-dashboard:
|
||||||
|
@ -16,7 +10,7 @@ build-dashboard:
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
test:
|
test:
|
||||||
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
|
rm -rf server/test/test.db && rm -rf test.db && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
|
||||||
test-mongodb:
|
test-mongodb:
|
||||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||||
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
|
||||||
|
@ -26,34 +20,18 @@ test-scylladb:
|
||||||
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
|
||||||
docker rm -vf authorizer_scylla_db
|
docker rm -vf authorizer_scylla_db
|
||||||
test-arangodb:
|
test-arangodb:
|
||||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
|
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||||
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
||||||
docker rm -vf authorizer_arangodb
|
docker rm -vf authorizer_arangodb
|
||||||
test-dynamodb:
|
|
||||||
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
|
||||||
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
|
|
||||||
docker rm -vf dynamodb-local-test
|
|
||||||
test-couchbase:
|
|
||||||
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
|
|
||||||
sh scripts/couchbase-test.sh
|
|
||||||
cd server && go clean --testcache && TEST_DBS="couchbase" go test -p 1 -v ./test
|
|
||||||
docker rm -vf couchbase-local-test
|
|
||||||
test-all-db:
|
test-all-db:
|
||||||
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal
|
rm -rf server/test/test.db && rm -rf test.db
|
||||||
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
||||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
|
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||||
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb" go test -p 1 -v ./test
|
||||||
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
|
|
||||||
sh scripts/couchbase-test.sh
|
|
||||||
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb,couchbase" go test -p 1 -v ./test
|
|
||||||
docker rm -vf authorizer_scylla_db
|
docker rm -vf authorizer_scylla_db
|
||||||
docker rm -vf authorizer_mongodb_db
|
docker rm -vf authorizer_mongodb_db
|
||||||
docker rm -vf authorizer_arangodb
|
docker rm -vf authorizer_arangodb
|
||||||
docker rm -vf dynamodb-local-test
|
generate:
|
||||||
docker rm -vf couchbase-local-test
|
cd server && go get github.com/99designs/gqlgen/cmd@v0.14.0 && go run github.com/99designs/gqlgen generate
|
||||||
generate-graphql:
|
|
||||||
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
|
|
||||||
generate-db-template:
|
|
||||||
cp -rf server/db/providers/provider_template server/db/providers/${dbname}
|
|
||||||
find server/db/providers/${dbname} -type f -exec sed -i -e 's/provider_template/${dbname}/g' {} \;
|
|
36
README.md
36
README.md
|
@ -7,17 +7,19 @@
|
||||||
Authorizer
|
Authorizer
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports 11+ databases including [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [YugaByte](https://www.yugabyte.com/), [MariaDB](https://mariadb.org/), [PlanetScale](https://planetscale.com/), [CassandraDB](https://cassandra.apache.org/_/index.html), [ScyllaDB](https://www.scylladb.com/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
|
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
|
||||||
|
|
||||||
For more information check:
|
## Table of contents
|
||||||
|
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
|
||||||
- [Docs](http://docs.authorizer.dev/)
|
- [Docs](http://docs.authorizer.dev/)
|
||||||
- [Discord Community](https://discord.gg/Zv2D5h6kkK)
|
- [Join Community](https://discord.gg/Zv2D5h6kkK)
|
||||||
- [Contributing Guide](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
|
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
<img src="https://docs.authorizer.dev/images/authorizer-arch.png" style="height:20em"/>
|
<img src="https://github.com/authorizerdev/authorizer/blob/main/assets/authorizer-architecture.png" style="height:20em"/>
|
||||||
|
|
||||||
#### We offer the following functionality
|
#### We offer the following functionality
|
||||||
|
|
||||||
|
@ -27,22 +29,20 @@ For more information check:
|
||||||
- ✅ OAuth2 and OpenID compatible APIs
|
- ✅ OAuth2 and OpenID compatible APIs
|
||||||
- ✅ APIs to update profile securely
|
- ✅ APIs to update profile securely
|
||||||
- ✅ Forgot password flow using email
|
- ✅ Forgot password flow using email
|
||||||
- ✅ Social logins (Google, Github, Facebook, LinkedIn, Apple more coming soon)
|
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
||||||
- ✅ Role-based access management
|
- ✅ Role-based access management
|
||||||
- ✅ Password-less login with magic link login
|
- ✅ Password-less login with magic link login
|
||||||
- ✅ Multi factor authentication
|
|
||||||
- ✅ Email templating
|
|
||||||
- ✅ Webhooks
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- [VueJS SDK](https://github.com/authorizerdev/authorizer-vue)
|
- 2 Factor authentication
|
||||||
- [Svelte SDK](https://github.com/authorizerdev/authorizer-svelte)
|
- VueJS SDK
|
||||||
- [Golang SDK](https://github.com/authorizerdev/authorizer-go)
|
- Svelte SDK
|
||||||
- React Native SDK
|
- React Native SDK
|
||||||
- Flutter SDK
|
- Flutter SDK
|
||||||
- Android Native SDK
|
- Android Native SDK
|
||||||
- iOS native SDK
|
- iOS native SDK
|
||||||
|
- Golang SDK
|
||||||
- Python SDK
|
- Python SDK
|
||||||
- PHP SDK
|
- PHP SDK
|
||||||
- WordPress plugin
|
- WordPress plugin
|
||||||
|
@ -63,13 +63,11 @@ For more information check:
|
||||||
|
|
||||||
Deploy production ready Authorizer instance using one click deployment options available below
|
Deploy production ready Authorizer instance using one click deployment options available below
|
||||||
|
|
||||||
| **Infra provider** | **One-click link** | **Additional information** |
|
| **Infra provider** | **One-click link** | **Additional information** |
|
||||||
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
||||||
| Railway.app | <a href="https://railway.app/new/template/nwXp1C?referralCode=FEF4uT"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
|
| Railway.app | <a href="https://railway.app/new/template?template=https://github.com/authorizerdev/authorizer-railway&plugins=postgresql,redis"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
|
||||||
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
|
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
|
||||||
| Render | [](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
|
| Render | [](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
|
||||||
| Koyeb | <a target="_blank" href="https://app.koyeb.com/deploy?name=authorizer&type=docker&image=docker.io/lakhansamani/authorizer&env[PORT]=8000&env[DATABASE_TYPE]=postgres&env[DATABASE_URL]=CHANGE_ME&ports=8000;http;/"><img alt="Deploy to Koyeb" src="https://www.koyeb.com/static/images/deploy/button.svg" /></a> | [docs](https://docs.authorizer.dev/deployment/koyeb) |
|
|
||||||
| RepoCloud | <a href="https://repocloud.io/details/?app_id=174"><img src="https://d16t0pc4846x52.cloudfront.net/deploy.png" alt="Deploy on RepoCloud"></a> | [docs](https://repocloud.io/details/?app_id=174) |
|
|
||||||
|
|
||||||
### Deploy Authorizer Using Source Code
|
### Deploy Authorizer Using Source Code
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"useTabs": true
|
|
||||||
}
|
|
965
app/package-lock.json
generated
965
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -5,14 +5,13 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
||||||
"start": "NODE_ENV=development node ./esbuild.config.js",
|
"start": "NODE_ENV=development node ./esbuild.config.js"
|
||||||
"format": "prettier --write 'src/**/*.(ts|tsx|js|jsx)'"
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^1.3.2",
|
"@authorizerdev/authorizer-react": "^0.26.0-beta.0",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
|
@ -20,12 +19,11 @@
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-is": "^17.0.2",
|
"react-is": "^17.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"styled-components": "^5.3.0",
|
"typescript": "^4.3.5",
|
||||||
"typescript": "^4.3.5"
|
"styled-components": "^5.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-router-dom": "^5.1.8",
|
"@types/react-router-dom": "^5.1.8",
|
||||||
"@types/styled-components": "^5.1.11",
|
"@types/styled-components": "^5.1.11"
|
||||||
"prettier": "2.7.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,626 +0,0 @@
|
||||||
lockfileVersion: 5.4
|
|
||||||
|
|
||||||
specifiers:
|
|
||||||
'@authorizerdev/authorizer-react': ^1.1.9
|
|
||||||
'@types/react': ^17.0.15
|
|
||||||
'@types/react-dom': ^17.0.9
|
|
||||||
'@types/react-router-dom': ^5.1.8
|
|
||||||
'@types/styled-components': ^5.1.11
|
|
||||||
esbuild: ^0.12.17
|
|
||||||
prettier: 2.7.1
|
|
||||||
react: ^17.0.2
|
|
||||||
react-dom: ^17.0.2
|
|
||||||
react-is: ^17.0.2
|
|
||||||
react-router-dom: ^5.2.0
|
|
||||||
styled-components: ^5.3.0
|
|
||||||
typescript: ^4.3.5
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
'@authorizerdev/authorizer-react': 1.1.9_react@17.0.2
|
|
||||||
'@types/react': 17.0.53
|
|
||||||
'@types/react-dom': 17.0.19
|
|
||||||
esbuild: 0.12.29
|
|
||||||
react: 17.0.2
|
|
||||||
react-dom: 17.0.2_react@17.0.2
|
|
||||||
react-is: 17.0.2
|
|
||||||
react-router-dom: 5.3.4_react@17.0.2
|
|
||||||
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
|
|
||||||
typescript: 4.9.5
|
|
||||||
|
|
||||||
devDependencies:
|
|
||||||
'@types/react-router-dom': 5.3.3
|
|
||||||
'@types/styled-components': 5.1.26
|
|
||||||
prettier: 2.7.1
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
/@authorizerdev/authorizer-js/1.2.1:
|
|
||||||
resolution: {integrity: sha512-/nFARvsHyZUsGFKrcYi8hgpnbThYR/NMJ2BJdQpWy/x7QsBnfLeCChBYWncbYHSIjFCa5PPKKfvhXM56HqVqsw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
cross-fetch: 3.1.5
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@authorizerdev/authorizer-react/1.1.9_react@17.0.2:
|
|
||||||
resolution: {integrity: sha512-BlB4ixEm9nf+yjZ9OqIWbx5fMTmzeByEsNDAd5iYkt6HB+3Sk53DGiO5h6SgJznzPyqAwl8yg6y/QgbZreDTFA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16'
|
|
||||||
dependencies:
|
|
||||||
'@authorizerdev/authorizer-js': 1.2.1
|
|
||||||
react: 17.0.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/code-frame/7.18.6:
|
|
||||||
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/highlight': 7.18.6
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/generator/7.21.3:
|
|
||||||
resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
'@jridgewell/gen-mapping': 0.3.2
|
|
||||||
'@jridgewell/trace-mapping': 0.3.17
|
|
||||||
jsesc: 2.5.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-annotate-as-pure/7.18.6:
|
|
||||||
resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-environment-visitor/7.18.9:
|
|
||||||
resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-function-name/7.21.0:
|
|
||||||
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/template': 7.20.7
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-hoist-variables/7.18.6:
|
|
||||||
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-module-imports/7.18.6:
|
|
||||||
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-split-export-declaration/7.18.6:
|
|
||||||
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-string-parser/7.19.4:
|
|
||||||
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/helper-validator-identifier/7.19.1:
|
|
||||||
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/highlight/7.18.6:
|
|
||||||
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/helper-validator-identifier': 7.19.1
|
|
||||||
chalk: 2.4.2
|
|
||||||
js-tokens: 4.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/parser/7.21.3:
|
|
||||||
resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/runtime/7.21.0:
|
|
||||||
resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime: 0.13.11
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/template/7.20.7:
|
|
||||||
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/code-frame': 7.18.6
|
|
||||||
'@babel/parser': 7.21.3
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/traverse/7.21.3_supports-color@5.5.0:
|
|
||||||
resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/code-frame': 7.18.6
|
|
||||||
'@babel/generator': 7.21.3
|
|
||||||
'@babel/helper-environment-visitor': 7.18.9
|
|
||||||
'@babel/helper-function-name': 7.21.0
|
|
||||||
'@babel/helper-hoist-variables': 7.18.6
|
|
||||||
'@babel/helper-split-export-declaration': 7.18.6
|
|
||||||
'@babel/parser': 7.21.3
|
|
||||||
'@babel/types': 7.21.3
|
|
||||||
debug: 4.3.4_supports-color@5.5.0
|
|
||||||
globals: 11.12.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@babel/types/7.21.3:
|
|
||||||
resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/helper-string-parser': 7.19.4
|
|
||||||
'@babel/helper-validator-identifier': 7.19.1
|
|
||||||
to-fast-properties: 2.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/is-prop-valid/1.2.0:
|
|
||||||
resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==}
|
|
||||||
dependencies:
|
|
||||||
'@emotion/memoize': 0.8.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/memoize/0.8.0:
|
|
||||||
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/stylis/0.8.5:
|
|
||||||
resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/unitless/0.7.5:
|
|
||||||
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@jridgewell/gen-mapping/0.3.2:
|
|
||||||
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
dependencies:
|
|
||||||
'@jridgewell/set-array': 1.1.2
|
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
|
||||||
'@jridgewell/trace-mapping': 0.3.17
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@jridgewell/resolve-uri/3.1.0:
|
|
||||||
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@jridgewell/set-array/1.1.2:
|
|
||||||
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@jridgewell/sourcemap-codec/1.4.14:
|
|
||||||
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@jridgewell/trace-mapping/0.3.17:
|
|
||||||
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
|
||||||
dependencies:
|
|
||||||
'@jridgewell/resolve-uri': 3.1.0
|
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/history/4.7.11:
|
|
||||||
resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/hoist-non-react-statics/3.3.1:
|
|
||||||
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 17.0.53
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/prop-types/15.7.5:
|
|
||||||
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
|
|
||||||
|
|
||||||
/@types/react-dom/17.0.19:
|
|
||||||
resolution: {integrity: sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ==}
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 17.0.53
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/react-router-dom/5.3.3:
|
|
||||||
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/history': 4.7.11
|
|
||||||
'@types/react': 17.0.53
|
|
||||||
'@types/react-router': 5.1.20
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/react-router/5.1.20:
|
|
||||||
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
|
|
||||||
dependencies:
|
|
||||||
'@types/history': 4.7.11
|
|
||||||
'@types/react': 17.0.53
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/react/17.0.53:
|
|
||||||
resolution: {integrity: sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/prop-types': 15.7.5
|
|
||||||
'@types/scheduler': 0.16.3
|
|
||||||
csstype: 3.1.1
|
|
||||||
|
|
||||||
/@types/scheduler/0.16.3:
|
|
||||||
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
|
|
||||||
|
|
||||||
/@types/styled-components/5.1.26:
|
|
||||||
resolution: {integrity: sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/hoist-non-react-statics': 3.3.1
|
|
||||||
'@types/react': 17.0.53
|
|
||||||
csstype: 3.1.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ansi-styles/3.2.1:
|
|
||||||
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dependencies:
|
|
||||||
color-convert: 1.9.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/babel-plugin-styled-components/2.0.7_styled-components@5.3.9:
|
|
||||||
resolution: {integrity: sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==}
|
|
||||||
peerDependencies:
|
|
||||||
styled-components: '>= 2'
|
|
||||||
dependencies:
|
|
||||||
'@babel/helper-annotate-as-pure': 7.18.6
|
|
||||||
'@babel/helper-module-imports': 7.18.6
|
|
||||||
babel-plugin-syntax-jsx: 6.18.0
|
|
||||||
lodash: 4.17.21
|
|
||||||
picomatch: 2.3.1
|
|
||||||
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/babel-plugin-syntax-jsx/6.18.0:
|
|
||||||
resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/camelize/1.0.1:
|
|
||||||
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/chalk/2.4.2:
|
|
||||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dependencies:
|
|
||||||
ansi-styles: 3.2.1
|
|
||||||
escape-string-regexp: 1.0.5
|
|
||||||
supports-color: 5.5.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/color-convert/1.9.3:
|
|
||||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
|
||||||
dependencies:
|
|
||||||
color-name: 1.1.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/color-name/1.1.3:
|
|
||||||
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cross-fetch/3.1.5:
|
|
||||||
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
|
|
||||||
dependencies:
|
|
||||||
node-fetch: 2.6.7
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/css-color-keywords/1.0.0:
|
|
||||||
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/css-to-react-native/3.2.0:
|
|
||||||
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
|
|
||||||
dependencies:
|
|
||||||
camelize: 1.0.1
|
|
||||||
css-color-keywords: 1.0.0
|
|
||||||
postcss-value-parser: 4.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/csstype/3.1.1:
|
|
||||||
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
|
||||||
|
|
||||||
/debug/4.3.4_supports-color@5.5.0:
|
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
|
||||||
engines: {node: '>=6.0'}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.2
|
|
||||||
supports-color: 5.5.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/esbuild/0.12.29:
|
|
||||||
resolution: {integrity: sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==}
|
|
||||||
hasBin: true
|
|
||||||
requiresBuild: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/escape-string-regexp/1.0.5:
|
|
||||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
|
||||||
engines: {node: '>=0.8.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/globals/11.12.0:
|
|
||||||
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/has-flag/3.0.0:
|
|
||||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/history/4.10.1:
|
|
||||||
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.21.0
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
resolve-pathname: 3.0.0
|
|
||||||
tiny-invariant: 1.3.1
|
|
||||||
tiny-warning: 1.0.3
|
|
||||||
value-equal: 1.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/hoist-non-react-statics/3.3.2:
|
|
||||||
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
|
|
||||||
dependencies:
|
|
||||||
react-is: 16.13.1
|
|
||||||
|
|
||||||
/isarray/0.0.1:
|
|
||||||
resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/js-tokens/4.0.0:
|
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jsesc/2.5.2:
|
|
||||||
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
hasBin: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash/4.17.21:
|
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/loose-envify/1.4.0:
|
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
js-tokens: 4.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms/2.1.2:
|
|
||||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/node-fetch/2.6.7:
|
|
||||||
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
|
|
||||||
engines: {node: 4.x || >=6.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
encoding: ^0.1.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
encoding:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
whatwg-url: 5.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/object-assign/4.1.1:
|
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/path-to-regexp/1.8.0:
|
|
||||||
resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==}
|
|
||||||
dependencies:
|
|
||||||
isarray: 0.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/picomatch/2.3.1:
|
|
||||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
|
||||||
engines: {node: '>=8.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/postcss-value-parser/4.2.0:
|
|
||||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/prettier/2.7.1:
|
|
||||||
resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
|
|
||||||
engines: {node: '>=10.13.0'}
|
|
||||||
hasBin: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/prop-types/15.8.1:
|
|
||||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
|
||||||
dependencies:
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
object-assign: 4.1.1
|
|
||||||
react-is: 16.13.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-dom/17.0.2_react@17.0.2:
|
|
||||||
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==}
|
|
||||||
peerDependencies:
|
|
||||||
react: 17.0.2
|
|
||||||
dependencies:
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
object-assign: 4.1.1
|
|
||||||
react: 17.0.2
|
|
||||||
scheduler: 0.20.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-is/16.13.1:
|
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
|
||||||
|
|
||||||
/react-is/17.0.2:
|
|
||||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-router-dom/5.3.4_react@17.0.2:
|
|
||||||
resolution: {integrity: sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=15'
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.21.0
|
|
||||||
history: 4.10.1
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 17.0.2
|
|
||||||
react-router: 5.3.4_react@17.0.2
|
|
||||||
tiny-invariant: 1.3.1
|
|
||||||
tiny-warning: 1.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-router/5.3.4_react@17.0.2:
|
|
||||||
resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=15'
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.21.0
|
|
||||||
history: 4.10.1
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
path-to-regexp: 1.8.0
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 17.0.2
|
|
||||||
react-is: 16.13.1
|
|
||||||
tiny-invariant: 1.3.1
|
|
||||||
tiny-warning: 1.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react/17.0.2:
|
|
||||||
resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dependencies:
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
object-assign: 4.1.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/regenerator-runtime/0.13.11:
|
|
||||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/resolve-pathname/3.0.0:
|
|
||||||
resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/scheduler/0.20.2:
|
|
||||||
resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
|
|
||||||
dependencies:
|
|
||||||
loose-envify: 1.4.0
|
|
||||||
object-assign: 4.1.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/shallowequal/1.1.0:
|
|
||||||
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/styled-components/5.3.9_fane7jikarojcev26y27hpbhu4:
|
|
||||||
resolution: {integrity: sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>= 16.8.0'
|
|
||||||
react-dom: '>= 16.8.0'
|
|
||||||
react-is: '>= 16.8.0'
|
|
||||||
dependencies:
|
|
||||||
'@babel/helper-module-imports': 7.18.6
|
|
||||||
'@babel/traverse': 7.21.3_supports-color@5.5.0
|
|
||||||
'@emotion/is-prop-valid': 1.2.0
|
|
||||||
'@emotion/stylis': 0.8.5
|
|
||||||
'@emotion/unitless': 0.7.5
|
|
||||||
babel-plugin-styled-components: 2.0.7_styled-components@5.3.9
|
|
||||||
css-to-react-native: 3.2.0
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
react: 17.0.2
|
|
||||||
react-dom: 17.0.2_react@17.0.2
|
|
||||||
react-is: 17.0.2
|
|
||||||
shallowequal: 1.1.0
|
|
||||||
supports-color: 5.5.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/supports-color/5.5.0:
|
|
||||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dependencies:
|
|
||||||
has-flag: 3.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tiny-invariant/1.3.1:
|
|
||||||
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tiny-warning/1.0.3:
|
|
||||||
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/to-fast-properties/2.0.0:
|
|
||||||
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tr46/0.0.3:
|
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/typescript/4.9.5:
|
|
||||||
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
|
|
||||||
engines: {node: '>=4.2.0'}
|
|
||||||
hasBin: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/value-equal/1.0.1:
|
|
||||||
resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/webidl-conversions/3.0.1:
|
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/whatwg-url/5.0.0:
|
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
|
||||||
dependencies:
|
|
||||||
tr46: 0.0.3
|
|
||||||
webidl-conversions: 3.0.1
|
|
||||||
dev: false
|
|
|
@ -4,12 +4,6 @@ import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
||||||
import Root from './Root';
|
import Root from './Root';
|
||||||
import { createRandomString } from './utils/common';
|
import { createRandomString } from './utils/common';
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
__authorizer__: any;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const state = searchParams.get('state') || createRandomString();
|
const state = searchParams.get('state') || createRandomString();
|
||||||
|
@ -27,12 +21,14 @@ export default function App() {
|
||||||
if (redirectURL) {
|
if (redirectURL) {
|
||||||
urlProps.redirectURL = redirectURL;
|
urlProps.redirectURL = redirectURL;
|
||||||
} else {
|
} else {
|
||||||
urlProps.redirectURL = window.location.href;
|
urlProps.redirectURL = window.location.origin + '/app';
|
||||||
}
|
}
|
||||||
const globalState: Record<string, string> = {
|
const globalState: Record<string, string> = {
|
||||||
|
// @ts-ignore
|
||||||
...window['__authorizer__'],
|
...window['__authorizer__'],
|
||||||
...urlProps,
|
...urlProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -53,7 +49,7 @@ export default function App() {
|
||||||
<img
|
<img
|
||||||
src={`${globalState.organizationLogo}`}
|
src={`${globalState.organizationLogo}`}
|
||||||
alt="logo"
|
alt="logo"
|
||||||
style={{ height: 60, objectFit: 'cover' }}
|
style={{ height: 60, width: 60, objectFit: 'cover' }}
|
||||||
/>
|
/>
|
||||||
<h1>{globalState.organizationName}</h1>
|
<h1>{globalState.organizationName}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,14 +32,12 @@ export default function Root({
|
||||||
const { token, loading, config } = useAuthorizer();
|
const { token, loading, config } = useAuthorizer();
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(
|
const searchParams = new URLSearchParams(
|
||||||
hasWindow() ? window.location.search : ``,
|
hasWindow() ? window.location.search : ``
|
||||||
);
|
);
|
||||||
const state = searchParams.get('state') || createRandomString();
|
const state = searchParams.get('state') || createRandomString();
|
||||||
const scope = searchParams.get('scope')
|
const scope = searchParams.get('scope')
|
||||||
? searchParams.get('scope')?.toString().split(' ')
|
? searchParams.get('scope')?.toString().split(' ')
|
||||||
: ['openid', 'profile', 'email'];
|
: ['openid', 'profile', 'email'];
|
||||||
const code = searchParams.get('code') || '';
|
|
||||||
const nonce = searchParams.get('nonce') || '';
|
|
||||||
|
|
||||||
const urlProps: Record<string, any> = {
|
const urlProps: Record<string, any> = {
|
||||||
state,
|
state,
|
||||||
|
@ -59,22 +57,10 @@ export default function Root({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
let redirectURL = config.redirectURL || '/app';
|
let redirectURL = config.redirectURL || '/app';
|
||||||
// let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
|
let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
|
||||||
// Note: If OIDC breaks in the future, use the above params
|
|
||||||
let params = `state=${globalState.state}`;
|
|
||||||
|
|
||||||
if (code !== '') {
|
|
||||||
params += `&code=${code}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nonce !== '') {
|
|
||||||
params += `&nonce=${nonce}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token.refresh_token) {
|
if (token.refresh_token) {
|
||||||
params += `&refresh_token=${token.refresh_token}`;
|
params += `&refresh_token=${token.refresh_token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(redirectURL);
|
const url = new URL(redirectURL);
|
||||||
if (redirectURL.includes('?')) {
|
if (redirectURL.includes('?')) {
|
||||||
redirectURL = `${redirectURL}&${params}`;
|
redirectURL = `${redirectURL}&${params}`;
|
||||||
|
@ -88,7 +74,7 @@ export default function Root({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
}, [token, config]);
|
}, [token]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <h1>Loading...</h1>;
|
return <h1>Loading...</h1>;
|
||||||
|
@ -114,7 +100,7 @@ export default function Root({
|
||||||
<Route path="/app" exact>
|
<Route path="/app" exact>
|
||||||
<Login urlProps={urlProps} />
|
<Login urlProps={urlProps} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/app/signup">
|
<Route path="/app/signup" exact>
|
||||||
<SignUp urlProps={urlProps} />
|
<SignUp urlProps={urlProps} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/app/reset-password">
|
<Route path="/app/reset-password">
|
||||||
|
|
|
@ -32,49 +32,35 @@ const FooterContent = styled.div`
|
||||||
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
||||||
const { config } = useAuthorizer();
|
const { config } = useAuthorizer();
|
||||||
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
|
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
|
||||||
const isBasicAuth = config.is_basic_authentication_enabled;
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{view === VIEW_TYPES.LOGIN && (
|
{view === VIEW_TYPES.LOGIN && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Login</h1>
|
<h1 style={{ textAlign: 'center' }}>Login</h1>
|
||||||
<AuthorizerSocialLogin urlProps={urlProps} />
|
|
||||||
<br />
|
<br />
|
||||||
{(config.is_basic_authentication_enabled ||
|
<AuthorizerSocialLogin urlProps={urlProps} />
|
||||||
config.is_mobile_basic_authentication_enabled) &&
|
{config.is_basic_authentication_enabled &&
|
||||||
!config.is_magic_link_login_enabled && (
|
!config.is_magic_link_login_enabled && (
|
||||||
<AuthorizerBasicAuthLogin urlProps={urlProps} />
|
<AuthorizerBasicAuthLogin urlProps={urlProps} />
|
||||||
)}
|
)}
|
||||||
{config.is_magic_link_login_enabled && (
|
{config.is_magic_link_login_enabled && (
|
||||||
<AuthorizerMagicLinkLogin urlProps={urlProps} />
|
<AuthorizerMagicLinkLogin urlProps={urlProps} />
|
||||||
)}
|
)}
|
||||||
{(config.is_basic_authentication_enabled ||
|
<Footer>
|
||||||
config.is_mobile_basic_authentication_enabled) &&
|
<Link
|
||||||
!config.is_magic_link_login_enabled && (
|
to="#"
|
||||||
<Footer>
|
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
|
||||||
<Link
|
style={{ marginBottom: 10 }}
|
||||||
to="#"
|
>
|
||||||
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
|
Forgot Password?
|
||||||
style={{ marginBottom: 10 }}
|
</Link>
|
||||||
>
|
</Footer>
|
||||||
Forgot Password?
|
|
||||||
</Link>
|
|
||||||
</Footer>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
|
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
|
||||||
<AuthorizerForgotPassword
|
<AuthorizerForgotPassword urlProps={urlProps} />
|
||||||
urlProps={{
|
|
||||||
...urlProps,
|
|
||||||
redirect_uri: `${window.location.origin}/app/reset-password`,
|
|
||||||
}}
|
|
||||||
onPasswordReset={() => {
|
|
||||||
setView(VIEW_TYPES.LOGIN);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Footer>
|
<Footer>
|
||||||
<Link
|
<Link
|
||||||
to="#"
|
to="#"
|
||||||
|
@ -90,7 +76,7 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
||||||
!config.is_magic_link_login_enabled &&
|
!config.is_magic_link_login_enabled &&
|
||||||
config.is_sign_up_enabled && (
|
config.is_sign_up_enabled && (
|
||||||
<FooterContent>
|
<FooterContent>
|
||||||
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
|
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
|
||||||
</FooterContent>
|
</FooterContent>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react';
|
import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ export default function SignUp({
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
||||||
<br />
|
<br />
|
||||||
<AuthorizerSocialLogin urlProps={urlProps} />
|
|
||||||
<AuthorizerSignup urlProps={urlProps} />
|
<AuthorizerSignup urlProps={urlProps} />
|
||||||
<FooterContent>
|
<FooterContent>
|
||||||
Already have an account? <Link to="/app"> Login</Link>
|
Already have an account? <Link to="/app"> Login</Link>
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
// colors: https://tailwindcss.com/docs/customizing-colors
|
// colors: https://tailwindcss.com/docs/customizing-colors
|
||||||
|
|
||||||
export const theme = {
|
export const theme = {
|
||||||
colors: {
|
colors: {
|
||||||
primary: '#3B82F6',
|
primary: '#3B82F6',
|
||||||
primaryDisabled: '#60A5FA',
|
primaryDisabled: '#60A5FA',
|
||||||
gray: '#D1D5DB',
|
gray: '#D1D5DB',
|
||||||
danger: '#DC2626',
|
danger: '#DC2626',
|
||||||
success: '#10B981',
|
success: '#10B981',
|
||||||
textColor: '#374151',
|
textColor: '#374151',
|
||||||
},
|
},
|
||||||
fonts: {
|
fonts: {
|
||||||
// typography
|
// typography
|
||||||
fontStack: '-apple-system, system-ui, sans-serif',
|
fontStack: '-apple-system, system-ui, sans-serif',
|
||||||
|
|
||||||
// font sizes
|
// font sizes
|
||||||
largeText: '18px',
|
largeText: '18px',
|
||||||
mediumText: '14px',
|
mediumText: '14px',
|
||||||
smallText: '12px',
|
smallText: '12px',
|
||||||
tinyText: '10px',
|
tinyText: '10px',
|
||||||
},
|
},
|
||||||
|
|
||||||
radius: {
|
radius: {
|
||||||
card: '5px',
|
card: '5px',
|
||||||
button: '5px',
|
button: '5px',
|
||||||
input: '5px',
|
input: '5px',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const createRandomString = () => {
|
||||||
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
|
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
|
||||||
let random = '';
|
let random = '';
|
||||||
const randomValues = Array.from(
|
const randomValues = Array.from(
|
||||||
getCrypto().getRandomValues(new Uint8Array(43)),
|
getCrypto().getRandomValues(new Uint8Array(43))
|
||||||
);
|
);
|
||||||
randomValues.forEach((v) => (random += charset[v % charset.length]));
|
randomValues.forEach((v) => (random += charset[v % charset.length]));
|
||||||
return random;
|
return random;
|
||||||
|
|
619
app/yarn.lock
619
app/yarn.lock
|
@ -1,619 +0,0 @@
|
||||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
||||||
# yarn lockfile v1
|
|
||||||
|
|
||||||
|
|
||||||
"@authorizerdev/authorizer-js@^2.0.3":
|
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.3.tgz"
|
|
||||||
integrity sha512-uencwr3Ea8mwfxVKDFf2ITRCRSmzvua+O2voRuiWQORtRQTgZQjkN3M+IEkEj+WP9M1iFIl+NDgzECsp8ptC/A==
|
|
||||||
dependencies:
|
|
||||||
cross-fetch "^3.1.5"
|
|
||||||
|
|
||||||
"@authorizerdev/authorizer-react@^1.3.2":
|
|
||||||
version "1.3.2"
|
|
||||||
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.3.2.tgz"
|
|
||||||
integrity sha512-3kMAygHBCa8Fc9Oo0lz1k88r+Pd6kx1PSn3NMYLwxQXy2jRt4xWn7iuGn+SDGFs3DzofaN71I61gRwQ+6dO1rw==
|
|
||||||
dependencies:
|
|
||||||
"@authorizerdev/authorizer-js" "^2.0.3"
|
|
||||||
validator "^13.11.0"
|
|
||||||
|
|
||||||
"@babel/code-frame@^7.22.13":
|
|
||||||
version "7.22.13"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
|
|
||||||
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
|
|
||||||
dependencies:
|
|
||||||
"@babel/highlight" "^7.22.13"
|
|
||||||
chalk "^2.4.2"
|
|
||||||
|
|
||||||
"@babel/generator@^7.23.0":
|
|
||||||
version "7.23.0"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz"
|
|
||||||
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.23.0"
|
|
||||||
"@jridgewell/gen-mapping" "^0.3.2"
|
|
||||||
"@jridgewell/trace-mapping" "^0.3.17"
|
|
||||||
jsesc "^2.5.1"
|
|
||||||
|
|
||||||
"@babel/helper-annotate-as-pure@^7.16.0":
|
|
||||||
version "7.16.7"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz"
|
|
||||||
integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.16.7"
|
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.22.20":
|
|
||||||
version "7.22.20"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
|
|
||||||
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.23.0":
|
|
||||||
version "7.23.0"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
|
|
||||||
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/template" "^7.22.15"
|
|
||||||
"@babel/types" "^7.23.0"
|
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@^7.22.5":
|
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
|
|
||||||
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.22.5"
|
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
|
|
||||||
version "7.16.7"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz"
|
|
||||||
integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.16.7"
|
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.22.6":
|
|
||||||
version "7.22.6"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
|
|
||||||
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.22.5"
|
|
||||||
|
|
||||||
"@babel/helper-string-parser@^7.22.5":
|
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
|
|
||||||
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
|
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@^7.22.20":
|
|
||||||
version "7.22.20"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
|
|
||||||
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
|
|
||||||
|
|
||||||
"@babel/highlight@^7.22.13":
|
|
||||||
version "7.22.20"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
|
|
||||||
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-validator-identifier" "^7.22.20"
|
|
||||||
chalk "^2.4.2"
|
|
||||||
js-tokens "^4.0.0"
|
|
||||||
|
|
||||||
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
|
|
||||||
version "7.23.0"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz"
|
|
||||||
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
|
||||||
|
|
||||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
|
|
||||||
version "7.14.8"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz"
|
|
||||||
integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.13.4"
|
|
||||||
|
|
||||||
"@babel/template@^7.22.15":
|
|
||||||
version "7.22.15"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
|
|
||||||
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame" "^7.22.13"
|
|
||||||
"@babel/parser" "^7.22.15"
|
|
||||||
"@babel/types" "^7.22.15"
|
|
||||||
|
|
||||||
"@babel/traverse@^7.4.5":
|
|
||||||
version "7.23.2"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz"
|
|
||||||
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame" "^7.22.13"
|
|
||||||
"@babel/generator" "^7.23.0"
|
|
||||||
"@babel/helper-environment-visitor" "^7.22.20"
|
|
||||||
"@babel/helper-function-name" "^7.23.0"
|
|
||||||
"@babel/helper-hoist-variables" "^7.22.5"
|
|
||||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
|
||||||
"@babel/parser" "^7.23.0"
|
|
||||||
"@babel/types" "^7.23.0"
|
|
||||||
debug "^4.1.0"
|
|
||||||
globals "^11.1.0"
|
|
||||||
|
|
||||||
"@babel/types@^7.16.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
|
|
||||||
version "7.23.0"
|
|
||||||
resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz"
|
|
||||||
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-string-parser" "^7.22.5"
|
|
||||||
"@babel/helper-validator-identifier" "^7.22.20"
|
|
||||||
to-fast-properties "^2.0.0"
|
|
||||||
|
|
||||||
"@emotion/is-prop-valid@^0.8.8":
|
|
||||||
version "0.8.8"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
|
|
||||||
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
|
|
||||||
dependencies:
|
|
||||||
"@emotion/memoize" "0.7.4"
|
|
||||||
|
|
||||||
"@emotion/memoize@0.7.4":
|
|
||||||
version "0.7.4"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
|
|
||||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
|
||||||
|
|
||||||
"@emotion/stylis@^0.8.4":
|
|
||||||
version "0.8.5"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
|
|
||||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
|
||||||
|
|
||||||
"@emotion/unitless@^0.7.4":
|
|
||||||
version "0.7.5"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
|
||||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
|
||||||
|
|
||||||
"@jridgewell/gen-mapping@^0.3.2":
|
|
||||||
version "0.3.3"
|
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz"
|
|
||||||
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
|
|
||||||
dependencies:
|
|
||||||
"@jridgewell/set-array" "^1.0.1"
|
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
|
||||||
"@jridgewell/trace-mapping" "^0.3.9"
|
|
||||||
|
|
||||||
"@jridgewell/resolve-uri@^3.1.0":
|
|
||||||
version "3.1.1"
|
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
|
|
||||||
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
|
|
||||||
|
|
||||||
"@jridgewell/set-array@^1.0.1":
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
|
|
||||||
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
|
||||||
version "1.4.15"
|
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
|
||||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
|
|
||||||
version "0.3.20"
|
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
|
|
||||||
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
|
|
||||||
dependencies:
|
|
||||||
"@jridgewell/resolve-uri" "^3.1.0"
|
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
|
||||||
|
|
||||||
"@types/history@*":
|
|
||||||
version "4.7.9"
|
|
||||||
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
|
|
||||||
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
|
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@*":
|
|
||||||
version "3.3.1"
|
|
||||||
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
|
|
||||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
|
||||||
dependencies:
|
|
||||||
"@types/react" "*"
|
|
||||||
hoist-non-react-statics "^3.3.0"
|
|
||||||
|
|
||||||
"@types/prop-types@*":
|
|
||||||
version "15.7.4"
|
|
||||||
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
|
|
||||||
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
|
||||||
|
|
||||||
"@types/react-dom@^17.0.9":
|
|
||||||
version "17.0.9"
|
|
||||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz"
|
|
||||||
integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
|
|
||||||
dependencies:
|
|
||||||
"@types/react" "*"
|
|
||||||
|
|
||||||
"@types/react-router-dom@^5.1.8":
|
|
||||||
version "5.1.8"
|
|
||||||
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz"
|
|
||||||
integrity sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==
|
|
||||||
dependencies:
|
|
||||||
"@types/history" "*"
|
|
||||||
"@types/react" "*"
|
|
||||||
"@types/react-router" "*"
|
|
||||||
|
|
||||||
"@types/react-router@*":
|
|
||||||
version "5.1.16"
|
|
||||||
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz"
|
|
||||||
integrity sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==
|
|
||||||
dependencies:
|
|
||||||
"@types/history" "*"
|
|
||||||
"@types/react" "*"
|
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^17.0.15":
|
|
||||||
version "17.0.15"
|
|
||||||
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz"
|
|
||||||
integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==
|
|
||||||
dependencies:
|
|
||||||
"@types/prop-types" "*"
|
|
||||||
"@types/scheduler" "*"
|
|
||||||
csstype "^3.0.2"
|
|
||||||
|
|
||||||
"@types/scheduler@*":
|
|
||||||
version "0.16.2"
|
|
||||||
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
|
|
||||||
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
|
||||||
|
|
||||||
"@types/styled-components@^5.1.11":
|
|
||||||
version "5.1.25"
|
|
||||||
resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz"
|
|
||||||
integrity sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/hoist-non-react-statics" "*"
|
|
||||||
"@types/react" "*"
|
|
||||||
csstype "^3.0.2"
|
|
||||||
|
|
||||||
ansi-styles@^3.2.1:
|
|
||||||
version "3.2.1"
|
|
||||||
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
|
|
||||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
|
||||||
dependencies:
|
|
||||||
color-convert "^1.9.0"
|
|
||||||
|
|
||||||
"babel-plugin-styled-components@>= 1.12.0":
|
|
||||||
version "2.0.2"
|
|
||||||
resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz"
|
|
||||||
integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-annotate-as-pure" "^7.16.0"
|
|
||||||
"@babel/helper-module-imports" "^7.16.0"
|
|
||||||
babel-plugin-syntax-jsx "^6.18.0"
|
|
||||||
lodash "^4.17.11"
|
|
||||||
|
|
||||||
babel-plugin-syntax-jsx@^6.18.0:
|
|
||||||
version "6.18.0"
|
|
||||||
resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz"
|
|
||||||
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
|
|
||||||
|
|
||||||
camelize@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
|
|
||||||
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
|
|
||||||
|
|
||||||
chalk@^2.4.2:
|
|
||||||
version "2.4.2"
|
|
||||||
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
|
|
||||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
|
||||||
dependencies:
|
|
||||||
ansi-styles "^3.2.1"
|
|
||||||
escape-string-regexp "^1.0.5"
|
|
||||||
supports-color "^5.3.0"
|
|
||||||
|
|
||||||
color-convert@^1.9.0:
|
|
||||||
version "1.9.3"
|
|
||||||
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
|
|
||||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
|
||||||
dependencies:
|
|
||||||
color-name "1.1.3"
|
|
||||||
|
|
||||||
color-name@1.1.3:
|
|
||||||
version "1.1.3"
|
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
|
||||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
|
||||||
|
|
||||||
cross-fetch@^3.1.5:
|
|
||||||
version "3.1.8"
|
|
||||||
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz"
|
|
||||||
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
|
|
||||||
dependencies:
|
|
||||||
node-fetch "^2.6.12"
|
|
||||||
|
|
||||||
css-color-keywords@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz"
|
|
||||||
integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
|
|
||||||
|
|
||||||
css-to-react-native@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz"
|
|
||||||
integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
|
|
||||||
dependencies:
|
|
||||||
camelize "^1.0.0"
|
|
||||||
css-color-keywords "^1.0.0"
|
|
||||||
postcss-value-parser "^4.0.2"
|
|
||||||
|
|
||||||
csstype@^3.0.2:
|
|
||||||
version "3.0.8"
|
|
||||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz"
|
|
||||||
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
|
||||||
|
|
||||||
debug@^4.1.0:
|
|
||||||
version "4.3.3"
|
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz"
|
|
||||||
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
|
||||||
dependencies:
|
|
||||||
ms "2.1.2"
|
|
||||||
|
|
||||||
esbuild@^0.12.17:
|
|
||||||
version "0.12.17"
|
|
||||||
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz"
|
|
||||||
integrity sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==
|
|
||||||
|
|
||||||
escape-string-regexp@^1.0.5:
|
|
||||||
version "1.0.5"
|
|
||||||
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
|
|
||||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
|
||||||
|
|
||||||
globals@^11.1.0:
|
|
||||||
version "11.12.0"
|
|
||||||
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
|
|
||||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
|
||||||
|
|
||||||
has-flag@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
|
|
||||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
|
||||||
|
|
||||||
history@^4.9.0:
|
|
||||||
version "4.10.1"
|
|
||||||
resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz"
|
|
||||||
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.1.2"
|
|
||||||
loose-envify "^1.2.0"
|
|
||||||
resolve-pathname "^3.0.0"
|
|
||||||
tiny-invariant "^1.0.2"
|
|
||||||
tiny-warning "^1.0.0"
|
|
||||||
value-equal "^1.0.1"
|
|
||||||
|
|
||||||
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
|
|
||||||
version "3.3.2"
|
|
||||||
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
|
|
||||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
|
||||||
dependencies:
|
|
||||||
react-is "^16.7.0"
|
|
||||||
|
|
||||||
isarray@0.0.1:
|
|
||||||
version "0.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
|
|
||||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
|
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
|
||||||
|
|
||||||
jsesc@^2.5.1:
|
|
||||||
version "2.5.2"
|
|
||||||
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
|
|
||||||
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
|
||||||
|
|
||||||
lodash@^4.17.11:
|
|
||||||
version "4.17.21"
|
|
||||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
|
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
|
||||||
|
|
||||||
loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
|
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
|
||||||
dependencies:
|
|
||||||
js-tokens "^3.0.0 || ^4.0.0"
|
|
||||||
|
|
||||||
mini-create-react-context@^0.4.0:
|
|
||||||
version "0.4.1"
|
|
||||||
resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz"
|
|
||||||
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.12.1"
|
|
||||||
tiny-warning "^1.0.3"
|
|
||||||
|
|
||||||
ms@2.1.2:
|
|
||||||
version "2.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
|
||||||
|
|
||||||
node-fetch@^2.6.12:
|
|
||||||
version "2.7.0"
|
|
||||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
|
||||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
|
||||||
dependencies:
|
|
||||||
whatwg-url "^5.0.0"
|
|
||||||
|
|
||||||
object-assign@^4.1.1:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
|
|
||||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
|
||||||
|
|
||||||
path-to-regexp@^1.7.0:
|
|
||||||
version "1.8.0"
|
|
||||||
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz"
|
|
||||||
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
|
|
||||||
dependencies:
|
|
||||||
isarray "0.0.1"
|
|
||||||
|
|
||||||
postcss-value-parser@^4.0.2:
|
|
||||||
version "4.2.0"
|
|
||||||
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
|
||||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
|
||||||
|
|
||||||
prettier@2.7.1:
|
|
||||||
version "2.7.1"
|
|
||||||
resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz"
|
|
||||||
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
|
|
||||||
|
|
||||||
prop-types@^15.0.0, prop-types@^15.6.2:
|
|
||||||
version "15.7.2"
|
|
||||||
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
|
|
||||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.4.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
react-is "^16.8.1"
|
|
||||||
|
|
||||||
react-dom@^17.0.2, "react-dom@>= 16.8.0":
|
|
||||||
version "17.0.2"
|
|
||||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
|
|
||||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
scheduler "^0.20.2"
|
|
||||||
|
|
||||||
react-is@^16.6.0:
|
|
||||||
version "16.13.1"
|
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
|
||||||
|
|
||||||
react-is@^16.7.0:
|
|
||||||
version "16.13.1"
|
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
|
||||||
|
|
||||||
react-is@^16.8.1:
|
|
||||||
version "16.13.1"
|
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
|
||||||
|
|
||||||
react-is@^17.0.2, "react-is@>= 16.8.0":
|
|
||||||
version "17.0.2"
|
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
|
|
||||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
|
||||||
|
|
||||||
react-router-dom@^5.2.0:
|
|
||||||
version "5.2.0"
|
|
||||||
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz"
|
|
||||||
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.1.2"
|
|
||||||
history "^4.9.0"
|
|
||||||
loose-envify "^1.3.1"
|
|
||||||
prop-types "^15.6.2"
|
|
||||||
react-router "5.2.0"
|
|
||||||
tiny-invariant "^1.0.2"
|
|
||||||
tiny-warning "^1.0.0"
|
|
||||||
|
|
||||||
react-router@5.2.0:
|
|
||||||
version "5.2.0"
|
|
||||||
resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz"
|
|
||||||
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.1.2"
|
|
||||||
history "^4.9.0"
|
|
||||||
hoist-non-react-statics "^3.1.0"
|
|
||||||
loose-envify "^1.3.1"
|
|
||||||
mini-create-react-context "^0.4.0"
|
|
||||||
path-to-regexp "^1.7.0"
|
|
||||||
prop-types "^15.6.2"
|
|
||||||
react-is "^16.6.0"
|
|
||||||
tiny-invariant "^1.0.2"
|
|
||||||
tiny-warning "^1.0.0"
|
|
||||||
|
|
||||||
"react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", react@^17.0.2, "react@>= 16.8.0", react@>=15, react@>=16, react@17.0.2:
|
|
||||||
version "17.0.2"
|
|
||||||
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
|
|
||||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
|
|
||||||
regenerator-runtime@^0.13.4:
|
|
||||||
version "0.13.9"
|
|
||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
|
||||||
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
|
||||||
|
|
||||||
resolve-pathname@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz"
|
|
||||||
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
|
|
||||||
|
|
||||||
scheduler@^0.20.2:
|
|
||||||
version "0.20.2"
|
|
||||||
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
|
|
||||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
|
|
||||||
shallowequal@^1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
|
|
||||||
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
|
|
||||||
|
|
||||||
styled-components@^5.3.0, "styled-components@>= 2":
|
|
||||||
version "5.3.3"
|
|
||||||
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
|
|
||||||
integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-module-imports" "^7.0.0"
|
|
||||||
"@babel/traverse" "^7.4.5"
|
|
||||||
"@emotion/is-prop-valid" "^0.8.8"
|
|
||||||
"@emotion/stylis" "^0.8.4"
|
|
||||||
"@emotion/unitless" "^0.7.4"
|
|
||||||
babel-plugin-styled-components ">= 1.12.0"
|
|
||||||
css-to-react-native "^3.0.0"
|
|
||||||
hoist-non-react-statics "^3.0.0"
|
|
||||||
shallowequal "^1.1.0"
|
|
||||||
supports-color "^5.5.0"
|
|
||||||
|
|
||||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
|
||||||
version "5.5.0"
|
|
||||||
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
|
|
||||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
|
||||||
dependencies:
|
|
||||||
has-flag "^3.0.0"
|
|
||||||
|
|
||||||
tiny-invariant@^1.0.2:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz"
|
|
||||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
|
||||||
|
|
||||||
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
|
|
||||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
|
||||||
|
|
||||||
to-fast-properties@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
|
|
||||||
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
|
|
||||||
|
|
||||||
tr46@~0.0.3:
|
|
||||||
version "0.0.3"
|
|
||||||
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
|
|
||||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
|
||||||
|
|
||||||
typescript@^4.3.5:
|
|
||||||
version "4.3.5"
|
|
||||||
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
|
|
||||||
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
|
||||||
|
|
||||||
validator@^13.11.0:
|
|
||||||
version "13.11.0"
|
|
||||||
resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz"
|
|
||||||
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
|
|
||||||
|
|
||||||
value-equal@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
|
|
||||||
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
|
|
||||||
|
|
||||||
webidl-conversions@^3.0.0:
|
|
||||||
version "3.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
|
|
||||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
|
||||||
|
|
||||||
whatwg-url@^5.0.0:
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
|
|
||||||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
|
||||||
dependencies:
|
|
||||||
tr46 "~0.0.3"
|
|
||||||
webidl-conversions "^3.0.0"
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"useTabs": true
|
|
||||||
}
|
|
2513
dashboard/package-lock.json
generated
2513
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -5,8 +5,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
||||||
"start": "NODE_ENV=development node ./esbuild.config.js",
|
"start": "NODE_ENV=development node ./esbuild.config.js"
|
||||||
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
|
@ -27,16 +26,10 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-draft-wysiwyg": "^1.15.0",
|
|
||||||
"react-dropzone": "^12.0.4",
|
"react-dropzone": "^12.0.4",
|
||||||
"react-email-editor": "^1.6.1",
|
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-router-dom": "^6.2.1",
|
"react-router-dom": "^6.2.1",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"urql": "^2.0.6"
|
"urql": "^2.0.6"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/react-email-editor": "^1.1.7",
|
|
||||||
"prettier": "2.7.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,106 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
MenuItem,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalFooter,
|
|
||||||
ModalHeader,
|
|
||||||
ModalOverlay,
|
|
||||||
useDisclosure,
|
|
||||||
Text,
|
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useClient } from 'urql';
|
|
||||||
import { FaRegTrashAlt } from 'react-icons/fa';
|
|
||||||
import { DeleteEmailTemplate } from '../graphql/mutation';
|
|
||||||
import { capitalizeFirstLetter } from '../utils';
|
|
||||||
|
|
||||||
interface deleteEmailTemplateModalInputPropTypes {
|
|
||||||
emailTemplateId: string;
|
|
||||||
eventName: string;
|
|
||||||
fetchEmailTemplatesData: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DeleteEmailTemplateModal = ({
|
|
||||||
emailTemplateId,
|
|
||||||
eventName,
|
|
||||||
fetchEmailTemplatesData,
|
|
||||||
}: deleteEmailTemplateModalInputPropTypes) => {
|
|
||||||
const client = useClient();
|
|
||||||
const toast = useToast();
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
|
|
||||||
const deleteHandler = async () => {
|
|
||||||
const res = await client
|
|
||||||
.mutation(DeleteEmailTemplate, { params: { id: emailTemplateId } })
|
|
||||||
.toPromise();
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else if (res.data?._delete_email_template) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.data?._delete_email_template.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
fetchEmailTemplatesData();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MenuItem onClick={onOpen}>Delete</MenuItem>
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent>
|
|
||||||
<ModalHeader>Delete Email Template</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<Text fontSize="md">Are you sure?</Text>
|
|
||||||
<Flex
|
|
||||||
padding="5%"
|
|
||||||
marginTop="5%"
|
|
||||||
marginBottom="2%"
|
|
||||||
border="1px solid #ff7875"
|
|
||||||
borderRadius="5px"
|
|
||||||
flexDirection="column"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm">
|
|
||||||
Email template for event <b>{eventName}</b> will be deleted
|
|
||||||
permanently!
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
leftIcon={<FaRegTrashAlt />}
|
|
||||||
colorScheme="red"
|
|
||||||
variant="solid"
|
|
||||||
onClick={deleteHandler}
|
|
||||||
isDisabled={false}
|
|
||||||
>
|
|
||||||
<Center h="100%" pt="5%">
|
|
||||||
Delete
|
|
||||||
</Center>
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeleteEmailTemplateModal;
|
|
|
@ -51,7 +51,7 @@ const DeleteUserModal = ({
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
title: capitalizeFirstLetter(res.error.message),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -60,7 +60,7 @@ const DeleteUserModal = ({
|
||||||
title: capitalizeFirstLetter(res.data?._delete_user.message),
|
title: capitalizeFirstLetter(res.data?._delete_user.message),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
|
|
|
@ -44,7 +44,7 @@ const DeleteWebhookModal = ({
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
title: capitalizeFirstLetter(res.error.message),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -53,7 +53,7 @@ const DeleteWebhookModal = ({
|
||||||
title: capitalizeFirstLetter(res.data?._delete_webhook.message),
|
title: capitalizeFirstLetter(res.data?._delete_webhook.message),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
|
@ -20,14 +20,13 @@ import { useClient } from 'urql';
|
||||||
import { FaSave } from 'react-icons/fa';
|
import { FaSave } from 'react-icons/fa';
|
||||||
import InputField from './InputField';
|
import InputField from './InputField';
|
||||||
import {
|
import {
|
||||||
|
ArrayInputType,
|
||||||
DateInputType,
|
DateInputType,
|
||||||
MultiSelectInputType,
|
|
||||||
SelectInputType,
|
SelectInputType,
|
||||||
TextInputType,
|
TextInputType,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { getObjectDiff } from '../utils';
|
import { getObjectDiff } from '../utils';
|
||||||
import { UpdateUser } from '../graphql/mutation';
|
import { UpdateUser } from '../graphql/mutation';
|
||||||
import { GetAvailableRolesQuery } from '../graphql/queries';
|
|
||||||
|
|
||||||
const GenderTypes = {
|
const GenderTypes = {
|
||||||
Undisclosed: null,
|
Undisclosed: null,
|
||||||
|
@ -58,9 +57,8 @@ const EditUserModal = ({
|
||||||
}) => {
|
}) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [userData, setUserData] = useState<userDataTypes>({
|
const [userData, setUserData] = React.useState<userDataTypes>({
|
||||||
id: '',
|
id: '',
|
||||||
email: '',
|
email: '',
|
||||||
given_name: '',
|
given_name: '',
|
||||||
|
@ -75,17 +73,7 @@ const EditUserModal = ({
|
||||||
});
|
});
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setUserData(user);
|
setUserData(user);
|
||||||
fetchAvailableRoles();
|
|
||||||
}, []);
|
}, []);
|
||||||
const fetchAvailableRoles = async () => {
|
|
||||||
const res = await client.query(GetAvailableRolesQuery).toPromise();
|
|
||||||
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
|
|
||||||
setAvailableRoles([
|
|
||||||
...res.data._env.ROLES,
|
|
||||||
...res.data._env.PROTECTED_ROLES,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const saveHandler = async () => {
|
const saveHandler = async () => {
|
||||||
const diff = getObjectDiff(user, userData);
|
const diff = getObjectDiff(user, userData);
|
||||||
const updatedUserData = diff.reduce(
|
const updatedUserData = diff.reduce(
|
||||||
|
@ -94,7 +82,7 @@ const EditUserModal = ({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
[property]: userData[property],
|
[property]: userData[property],
|
||||||
}),
|
}),
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
const res = await client
|
const res = await client
|
||||||
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
|
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
|
||||||
|
@ -104,14 +92,14 @@ const EditUserModal = ({
|
||||||
title: 'User data update failed',
|
title: 'User data update failed',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
} else if (res.data?._update_user?.id) {
|
} else if (res.data?._update_user?.id) {
|
||||||
toast({
|
toast({
|
||||||
title: 'User data update successful',
|
title: 'User data update successful',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
|
@ -233,8 +221,7 @@ const EditUserModal = ({
|
||||||
<InputField
|
<InputField
|
||||||
variables={userData}
|
variables={userData}
|
||||||
setVariables={setUserData}
|
setVariables={setUserData}
|
||||||
availableRoles={availableRoles}
|
inputType={ArrayInputType.USER_ROLES}
|
||||||
inputType={MultiSelectInputType.USER_ROLES}
|
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -1,65 +1,65 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Flex, Stack, Text, useMediaQuery } from '@chakra-ui/react';
|
import { Flex, Stack, Text, useMediaQuery } from "@chakra-ui/react";
|
||||||
import InputField from '../../components/InputField';
|
import InputField from "../../components/InputField";
|
||||||
import { TextInputType, TextAreaInputType } from '../../constants';
|
import { TextInputType, TextAreaInputType } from "../../constants";
|
||||||
|
|
||||||
const AccessToken = ({ variables, setVariables }: any) => {
|
const AccessToken = ({ variables, setVariables }: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{" "}
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||||
Access Token
|
Access Token
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<Stack spacing={6} padding="2% 0%">
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '50%'}
|
w={isNotSmallerScreen ? "30%" : "50%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">Access Token Expiry Time:</Text>
|
<Text fontSize="sm">Access Token Expiry Time:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
|
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
|
||||||
placeholder="0h15m0s"
|
placeholder="0h15m0s"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '60%'}
|
w={isNotSmallerScreen ? "30%" : "60%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
direction="column"
|
direction="column"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">Custom Scripts:</Text>
|
<Text fontSize="sm">Custom Scripts:</Text>
|
||||||
<Text fontSize="xs" color="blackAlpha.500">
|
<Text fontSize="xs" color="blackAlpha.500">
|
||||||
(Used to add custom fields in ID token)
|
(Used to add custom fields in ID token)
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
|
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
|
||||||
placeholder="Add script here"
|
placeholder="Add script here"
|
||||||
minH="25vh"
|
minH="25vh"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AccessToken;
|
export default AccessToken;
|
|
@ -1,35 +1,35 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
|
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||||
import InputField from '../../components/InputField';
|
import InputField from "../../components/InputField";
|
||||||
import { ArrayInputType } from '../../constants';
|
import { ArrayInputType} from "../../constants";
|
||||||
|
|
||||||
const DomainWhiteListing = ({ variables, setVariables }: any) => {
|
const DomainWhiteListing = ({ variables, setVariables }: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{" "}
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||||
Domain White Listing
|
Domain White Listing
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<Stack spacing={6} padding="2% 0%">
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Allowed Origins:</Text>
|
<Text fontSize="sm">Allowed Origins:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={ArrayInputType.ALLOWED_ORIGINS}
|
inputType={ArrayInputType.ALLOWED_ORIGINS}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DomainWhiteListing;
|
export default DomainWhiteListing;
|
|
@ -1,150 +1,114 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
|
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||||
import InputField from '../../components/InputField';
|
import InputField from "../../components/InputField";
|
||||||
import { TextInputType, HiddenInputType } from '../../constants';
|
import { TextInputType, HiddenInputType} from "../../constants";
|
||||||
const EmailConfigurations = ({
|
const EmailConfigurations = ({
|
||||||
variables,
|
variables,
|
||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{" "}
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||||
Email Configurations
|
Email Configurations
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<Stack spacing={6} padding="2% 0%">
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">SMTP Host:</Text>
|
<Text fontSize="sm">SMTP Host:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.SMTP_HOST}
|
inputType={TextInputType.SMTP_HOST}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">SMTP Port:</Text>
|
<Text fontSize="sm">SMTP Port:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.SMTP_PORT}
|
inputType={TextInputType.SMTP_PORT}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">SMTP Local Name:</Text>
|
<Text fontSize="sm">SMTP Username:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.SMTP_LOCAL_NAME}
|
inputType={TextInputType.SMTP_USERNAME}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">SMTP Username:</Text>
|
<Text fontSize="sm">SMTP Password:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.SMTP_USERNAME}
|
fieldVisibility={fieldVisibility}
|
||||||
/>
|
setFieldVisibility={setFieldVisibility}
|
||||||
</Center>
|
inputType={HiddenInputType.SMTP_PASSWORD}
|
||||||
</Flex>
|
/>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
</Center>
|
||||||
<Flex
|
</Flex>
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
justifyContent="start"
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
alignItems="center"
|
<Text fontSize="sm">From Email:</Text>
|
||||||
>
|
</Flex>
|
||||||
<Text fontSize="sm">SMTP Password:</Text>
|
<Center
|
||||||
</Flex>
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
<Center
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
>
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
<InputField
|
||||||
>
|
borderRadius={5}
|
||||||
<InputField
|
variables={variables}
|
||||||
borderRadius={5}
|
setVariables={setVariables}
|
||||||
variables={variables}
|
inputType={TextInputType.SENDER_EMAIL}
|
||||||
setVariables={setVariables}
|
/>
|
||||||
fieldVisibility={fieldVisibility}
|
</Center>
|
||||||
setFieldVisibility={setFieldVisibility}
|
</Flex>
|
||||||
inputType={HiddenInputType.SMTP_PASSWORD}
|
</Stack>
|
||||||
/>
|
</div>
|
||||||
</Center>
|
);
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">From Email:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.SENDER_EMAIL}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Sender Name:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.SENDER_NAME}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EmailConfigurations;
|
export default EmailConfigurations;
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Divider, Flex, Stack, Text } from '@chakra-ui/react';
|
import { Flex, Stack, Text } from '@chakra-ui/react';
|
||||||
import InputField from '../InputField';
|
import InputField from '../InputField';
|
||||||
import { SwitchInputType } from '../../constants';
|
import { SwitchInputType } from '../../constants';
|
||||||
|
|
||||||
|
@ -8,216 +8,78 @@ const Features = ({ variables, setVariables }: any) => {
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{' '}
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||||
Features
|
Disable Features
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6}>
|
<Stack spacing={6} padding="2% 0%">
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Login Page:</Text>
|
<Text fontSize="sm">Disable Login Page:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start">
|
<Flex justifyContent="start">
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
|
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
|
||||||
hasReversedValue
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Email Verification:</Text>
|
<Text fontSize="sm">Disable Email Verification:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start">
|
<Flex justifyContent="start">
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
|
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
|
||||||
hasReversedValue
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Magic Login Link:</Text>
|
<Text fontSize="sm">Disable Magic Login Link:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start">
|
<Flex justifyContent="start">
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
|
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
|
||||||
hasReversedValue
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Email Basic Authentication:</Text>
|
<Text fontSize="sm">Disable Basic Authentication:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start">
|
<Flex justifyContent="start">
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
|
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
|
||||||
hasReversedValue
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Mobile Basic Authentication:</Text>
|
<Text fontSize="sm">Disable Sign Up:</Text>
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start">
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_MOBILE_BASIC_AUTHENTICATION}
|
|
||||||
hasReversedValue
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Sign Up:</Text>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start" mb={3}>
|
<Flex justifyContent="start" mb={3}>
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={SwitchInputType.DISABLE_SIGN_UP}
|
inputType={SwitchInputType.DISABLE_SIGN_UP}
|
||||||
hasReversedValue
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Strong Password:</Text>
|
<Text fontSize="sm">Disable Strong Password:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start" mb={3}>
|
<Flex justifyContent="start" mb={3}>
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={SwitchInputType.DISABLE_STRONG_PASSWORD}
|
inputType={SwitchInputType.DISABLE_STRONG_PASSWORD}
|
||||||
hasReversedValue
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
|
||||||
<Text fontSize="sm">Multi Factor Authentication (MFA):</Text>
|
|
||||||
<Text fontSize="x-small">
|
|
||||||
Note: Enabling this will ignore Enforcing MFA shown below and will
|
|
||||||
also ignore the user MFA setting.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex justifyContent="start" mb={3}>
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_MULTI_FACTOR_AUTHENTICATION}
|
|
||||||
hasReversedValue
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
|
||||||
<Text fontSize="sm">Time Based OTP (TOTP):</Text>
|
|
||||||
<Text fontSize="x-small">Note: to enable totp mfa</Text>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex justifyContent="start" mb={3}>
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_TOTP_LOGIN}
|
|
||||||
hasReversedValue
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
|
||||||
<Text fontSize="sm">EMAIL OTP:</Text>
|
|
||||||
<Text fontSize="x-small">Note: to enable email otp mfa</Text>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex justifyContent="start" mb={3}>
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_MAIL_OTP_LOGIN}
|
|
||||||
hasReversedValue
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
|
||||||
<Text fontSize="sm">
|
|
||||||
Enforce Multi Factor Authentication (MFA):
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="x-small">
|
|
||||||
Note: If you disable enforcing after it was enabled, it will still
|
|
||||||
keep MFA enabled for older users.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start" mb={3}>
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.ENFORCE_MULTI_FACTOR_AUTHENTICATION}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Playground:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start">
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.DISABLE_PLAYGROUND}
|
|
||||||
hasReversedValue
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
<Divider paddingY={5} />
|
|
||||||
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
|
|
||||||
Cookie Security Features
|
|
||||||
</Text>
|
|
||||||
<Stack spacing={6}>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
|
||||||
<Text fontSize="sm">Use Secure App Cookie:</Text>
|
|
||||||
<Text fontSize="x-small">
|
|
||||||
Note: If you set this to insecure, it will set{' '}
|
|
||||||
<code>sameSite</code> property of cookie to <code>lax</code> mode
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start">
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.APP_COOKIE_SECURE}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex>
|
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
|
||||||
<Text fontSize="sm">Use Secure Admin Cookie:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent="start">
|
|
||||||
<InputField
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SwitchInputType.ADMIN_COOKIE_SECURE}
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -1,200 +1,154 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
|
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||||
import {
|
import {
|
||||||
Flex,
|
HiddenInputType,
|
||||||
Stack,
|
TextInputType,
|
||||||
Center,
|
TextAreaInputType,
|
||||||
Text,
|
} from "../../constants";
|
||||||
useMediaQuery,
|
import GenerateKeysModal from "../GenerateKeysModal";
|
||||||
Button,
|
import InputField from "../InputField";
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
HiddenInputType,
|
|
||||||
TextInputType,
|
|
||||||
TextAreaInputType,
|
|
||||||
} from '../../constants';
|
|
||||||
import GenerateKeysModal from '../GenerateKeysModal';
|
|
||||||
import InputField from '../InputField';
|
|
||||||
import { copyTextToClipboard } from '../../utils';
|
|
||||||
|
|
||||||
const JSTConfigurations = ({
|
const JSTConfigurations = ({
|
||||||
variables,
|
variables,
|
||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
SelectInputType,
|
SelectInputType,
|
||||||
getData,
|
getData,
|
||||||
HMACEncryptionType,
|
HMACEncryptionType,
|
||||||
RSAEncryptionType,
|
RSAEncryptionType,
|
||||||
ECDSAEncryptionType,
|
ECDSAEncryptionType,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const copyJSON = async () => {
|
return (
|
||||||
try {
|
<div>
|
||||||
await copyTextToClipboard(
|
{" "}
|
||||||
JSON.stringify({
|
<Flex
|
||||||
type: variables.JWT_TYPE,
|
borderRadius={5}
|
||||||
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET,
|
width="100%"
|
||||||
}),
|
justifyContent="space-between"
|
||||||
);
|
alignItems="center"
|
||||||
toast({
|
paddingTop="2%"
|
||||||
title: `JWT config copied successfully`,
|
>
|
||||||
isClosable: true,
|
<Text
|
||||||
status: 'success',
|
fontSize={isNotSmallerScreen ? "md" : "sm"}
|
||||||
position: 'top-right',
|
fontWeight="bold"
|
||||||
});
|
mb={5}
|
||||||
} catch (err) {
|
>
|
||||||
console.error({
|
JWT (JSON Web Tokens) Configurations
|
||||||
message: `Failed to copy JWT config`,
|
</Text>
|
||||||
error: err,
|
<Flex mb={7}>
|
||||||
});
|
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
|
||||||
toast({
|
</Flex>
|
||||||
title: `Failed to copy JWT config`,
|
</Flex>
|
||||||
isClosable: true,
|
<Stack spacing={6} padding="2% 0%">
|
||||||
status: 'error',
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
position: 'top-right',
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
});
|
<Text fontSize="sm">JWT Type:</Text>
|
||||||
}
|
</Flex>
|
||||||
};
|
<Flex
|
||||||
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
return (
|
mt={isNotSmallerScreen ? "0" : "2"}
|
||||||
<div>
|
>
|
||||||
<Flex
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
width="100%"
|
variables={variables}
|
||||||
justifyContent="space-between"
|
setVariables={setVariables}
|
||||||
alignItems="center"
|
inputType={SelectInputType}
|
||||||
paddingTop="2%"
|
value={SelectInputType}
|
||||||
>
|
options={{
|
||||||
<Text
|
...HMACEncryptionType,
|
||||||
fontSize={isNotSmallerScreen ? 'md' : 'sm'}
|
...RSAEncryptionType,
|
||||||
fontWeight="bold"
|
...ECDSAEncryptionType,
|
||||||
mb={5}
|
}}
|
||||||
>
|
/>
|
||||||
JWT (JSON Web Tokens) Configurations
|
</Flex>
|
||||||
</Text>
|
</Flex>
|
||||||
<Flex mb={7}>
|
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
|
||||||
<Button
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
colorScheme="blue"
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
h="1.75rem"
|
<Text fontSize="sm">JWT Secret</Text>
|
||||||
size="sm"
|
</Flex>
|
||||||
variant="ghost"
|
<Center
|
||||||
onClick={copyJSON}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
>
|
mt={isNotSmallerScreen ? "0" : "2"}
|
||||||
Copy As JSON Config
|
>
|
||||||
</Button>
|
<InputField
|
||||||
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
|
borderRadius={5}
|
||||||
</Flex>
|
variables={variables}
|
||||||
</Flex>
|
setVariables={setVariables}
|
||||||
<Stack spacing={6} padding="2% 0%">
|
fieldVisibility={fieldVisibility}
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
setFieldVisibility={setFieldVisibility}
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
inputType={HiddenInputType.JWT_SECRET}
|
||||||
<Text fontSize="sm">JWT Type:</Text>
|
/>
|
||||||
</Flex>
|
</Center>
|
||||||
<Flex
|
</Flex>
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
) : (
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
<>
|
||||||
>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<InputField
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
borderRadius={5}
|
<Text fontSize="sm">Public Key</Text>
|
||||||
variables={variables}
|
</Flex>
|
||||||
setVariables={setVariables}
|
<Center
|
||||||
inputType={SelectInputType}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
value={SelectInputType}
|
mt={isNotSmallerScreen ? "0" : "2"}
|
||||||
options={{
|
>
|
||||||
...HMACEncryptionType,
|
<InputField
|
||||||
...RSAEncryptionType,
|
borderRadius={5}
|
||||||
...ECDSAEncryptionType,
|
variables={variables}
|
||||||
}}
|
setVariables={setVariables}
|
||||||
/>
|
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
||||||
</Flex>
|
placeholder="Add public key here"
|
||||||
</Flex>
|
minH="25vh"
|
||||||
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
|
/>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
</Center>
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
</Flex>
|
||||||
<Text fontSize="sm">JWT Secret</Text>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
</Flex>
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
<Center
|
<Text fontSize="sm">Private Key</Text>
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
</Flex>
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
<Center
|
||||||
>
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
<InputField
|
mt={isNotSmallerScreen ? "0" : "2"}
|
||||||
borderRadius={5}
|
>
|
||||||
variables={variables}
|
<InputField
|
||||||
setVariables={setVariables}
|
borderRadius={5}
|
||||||
fieldVisibility={fieldVisibility}
|
variables={variables}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setVariables={setVariables}
|
||||||
inputType={HiddenInputType.JWT_SECRET}
|
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
|
||||||
/>
|
placeholder="Add private key here"
|
||||||
</Center>
|
minH="25vh"
|
||||||
</Flex>
|
/>
|
||||||
) : (
|
</Center>
|
||||||
<>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
</>
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
)}
|
||||||
<Text fontSize="sm">Public Key</Text>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
</Flex>
|
<Flex
|
||||||
<Center
|
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
justifyContent="start"
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<InputField
|
<Text fontSize="sm" orientation="vertical">
|
||||||
borderRadius={5}
|
JWT Role Claim:
|
||||||
variables={variables}
|
</Text>
|
||||||
setVariables={setVariables}
|
</Flex>
|
||||||
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
<Center
|
||||||
placeholder="Add public key here"
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
minH="25vh"
|
mt={isNotSmallerScreen ? "0" : "2"}
|
||||||
/>
|
>
|
||||||
</Center>
|
<InputField
|
||||||
</Flex>
|
borderRadius={5}
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
variables={variables}
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
setVariables={setVariables}
|
||||||
<Text fontSize="sm">Private Key</Text>
|
inputType={TextInputType.JWT_ROLE_CLAIM}
|
||||||
</Flex>
|
/>
|
||||||
<Center
|
</Center>
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
</Flex>
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
</Stack>
|
||||||
>
|
</div>
|
||||||
<InputField
|
);
|
||||||
borderRadius={5}
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
|
|
||||||
placeholder="Add private key here"
|
|
||||||
minH="25vh"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Flex
|
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
|
||||||
justifyContent="start"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm" orientation="vertical">
|
|
||||||
JWT Role Claim:
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={variables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.JWT_ROLE_CLAIM}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JSTConfigurations;
|
export default JSTConfigurations;
|
|
@ -15,18 +15,8 @@ import {
|
||||||
FaFacebookF,
|
FaFacebookF,
|
||||||
FaLinkedin,
|
FaLinkedin,
|
||||||
FaApple,
|
FaApple,
|
||||||
FaTwitter,
|
|
||||||
FaMicrosoft,
|
|
||||||
FaTwitch,
|
|
||||||
FaDiscord,
|
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import {
|
import { TextInputType, HiddenInputType } from '../../constants';
|
||||||
TextInputType,
|
|
||||||
HiddenInputType,
|
|
||||||
ResponseModes,
|
|
||||||
ResponseTypes,
|
|
||||||
SelectInputType,
|
|
||||||
} from '../../constants';
|
|
||||||
|
|
||||||
const OAuthConfig = ({
|
const OAuthConfig = ({
|
||||||
envVariables,
|
envVariables,
|
||||||
|
@ -78,42 +68,6 @@ const OAuthConfig = ({
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Default Response Type:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_TYPE}
|
|
||||||
value={SelectInputType}
|
|
||||||
options={ResponseTypes}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
|
||||||
<Text fontSize="sm">Default Response Mode:</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '2'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_MODE}
|
|
||||||
value={SelectInputType}
|
|
||||||
options={ResponseModes}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider mt={5} mb={2} color="blackAlpha.700" />
|
<Divider mt={5} mb={2} color="blackAlpha.700" />
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
|
||||||
|
@ -310,212 +264,6 @@ const OAuthConfig = ({
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
|
||||||
h="35px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #3b5998"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaDiscord style={{ color: '#7289da' }} />
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
marginRight="1.5%"
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.DISCORD_CLIENT_ID}
|
|
||||||
placeholder="Discord Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.DISCORD_CLIENT_SECRET}
|
|
||||||
placeholder="Discord Client Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
|
||||||
h="35px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #3b5998"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaTwitter />
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
marginRight="1.5%"
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.TWITTER_CLIENT_ID}
|
|
||||||
placeholder="Twitter Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.TWITTER_CLIENT_SECRET}
|
|
||||||
placeholder="Twitter Client Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
|
||||||
h="35px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #3b5998"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaMicrosoft />
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '35%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
marginRight="1.5%"
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID}
|
|
||||||
placeholder="Microsoft Active Directory TenantID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '35%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
marginRight="1.5%"
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.MICROSOFT_CLIENT_ID}
|
|
||||||
placeholder="Microsoft Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.MICROSOFT_CLIENT_SECRET}
|
|
||||||
placeholder="Microsoft Client Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
|
||||||
h="35px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #3b5998"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<FaTwitch />
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
marginRight="1.5%"
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.TWITCH_CLIENT_ID}
|
|
||||||
placeholder="Twitch Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.TWITCH_CLIENT_SECRET}
|
|
||||||
placeholder="Twitch Client Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
|
||||||
h="35px"
|
|
||||||
marginRight="1.5%"
|
|
||||||
border="1px solid #3b5998"
|
|
||||||
borderRadius="5px"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://authorizer.dev/_next/image?url=%2Fimages%2Froblox.png&w=25&q=25"
|
|
||||||
alt="Roblox"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
marginRight="1.5%"
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
inputType={TextInputType.ROBLOX_CLIENT_ID}
|
|
||||||
placeholder="Roblox Client ID"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
<Center
|
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
|
||||||
>
|
|
||||||
<InputField
|
|
||||||
borderRadius={5}
|
|
||||||
variables={envVariables}
|
|
||||||
setVariables={setVariables}
|
|
||||||
fieldVisibility={fieldVisibility}
|
|
||||||
setFieldVisibility={setFieldVisibility}
|
|
||||||
inputType={HiddenInputType.ROBLOX_CLIENT_SECRET}
|
|
||||||
placeholder="Roblox Client Secret"
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</Flex>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
|
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
||||||
import InputField from '../InputField';
|
import InputField from "../InputField";
|
||||||
import { TextInputType } from '../../constants';
|
import { TextInputType } from "../../constants";
|
||||||
|
|
||||||
const OrganizationInfo = ({ variables, setVariables }: any) => {
|
const OrganizationInfo = ({ variables, setVariables }: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{" "}
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||||
Organization Information
|
Organization Information
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<Stack spacing={6} padding="2% 0%">
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">Organization Name:</Text>
|
<Text fontSize="sm">Organization Name:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.ORGANIZATION_NAME}
|
inputType={TextInputType.ORGANIZATION_NAME}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">Organization Logo:</Text>
|
<Text fontSize="sm">Organization Logo:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={TextInputType.ORGANIZATION_LOGO}
|
inputType={TextInputType.ORGANIZATION_LOGO}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrganizationInfo;
|
export default OrganizationInfo;
|
|
@ -1,138 +1,138 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
Flex,
|
Flex,
|
||||||
Stack,
|
Stack,
|
||||||
Center,
|
Center,
|
||||||
Text,
|
Text,
|
||||||
Input,
|
Input,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
InputRightElement,
|
InputRightElement,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from '@chakra-ui/react';
|
} from "@chakra-ui/react";
|
||||||
import { FaRegEyeSlash, FaRegEye } from 'react-icons/fa';
|
import { FaRegEyeSlash, FaRegEye } from "react-icons/fa";
|
||||||
import InputField from '../InputField';
|
import InputField from "../InputField";
|
||||||
import { HiddenInputType } from '../../constants';
|
import { HiddenInputType } from "../../constants";
|
||||||
const SecurityAdminSecret = ({
|
const SecurityAdminSecret = ({
|
||||||
variables,
|
variables,
|
||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
validateAdminSecretHandler,
|
validateAdminSecretHandler,
|
||||||
adminSecret,
|
adminSecret,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{" "}
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
||||||
Security (Admin Secret)
|
Security (Admin Secret)
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
spacing={6}
|
spacing={6}
|
||||||
padding="0 5%"
|
padding="0 5%"
|
||||||
marginTop="3%"
|
marginTop="3%"
|
||||||
border="1px solid #ff7875"
|
border="1px solid #ff7875"
|
||||||
borderRadius="5px"
|
borderRadius="5px"
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
marginTop={isNotSmallerScreen ? '3%' : '5%'}
|
marginTop={isNotSmallerScreen ? "3%" : "5%"}
|
||||||
direction={isNotSmallerScreen ? 'row' : 'column'}
|
direction={isNotSmallerScreen ? "row" : "column"}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
mt={3}
|
mt={3}
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
w={isNotSmallerScreen ? "30%" : "40%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">Old Admin Secret:</Text>
|
<Text fontSize="sm">Old Admin Secret:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
size="sm"
|
size="sm"
|
||||||
placeholder="Enter Old Admin Secret"
|
placeholder="Enter Old Admin Secret"
|
||||||
value={adminSecret.value as string}
|
value={adminSecret.value as string}
|
||||||
onChange={(event: any) => validateAdminSecretHandler(event)}
|
onChange={(event: any) => validateAdminSecretHandler(event)}
|
||||||
type={
|
type={
|
||||||
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
|
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
|
||||||
? 'password'
|
? "password"
|
||||||
: 'text'
|
: "text"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<InputRightElement
|
<InputRightElement
|
||||||
right="5px"
|
right="5px"
|
||||||
children={
|
children={
|
||||||
<Flex>
|
<Flex>
|
||||||
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
|
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
|
||||||
<Center
|
<Center
|
||||||
w="25px"
|
w="25px"
|
||||||
margin="0 1.5%"
|
margin="0 1.5%"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setFieldVisibility({
|
setFieldVisibility({
|
||||||
...fieldVisibility,
|
...fieldVisibility,
|
||||||
[HiddenInputType.OLD_ADMIN_SECRET]: false,
|
[HiddenInputType.OLD_ADMIN_SECRET]: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FaRegEyeSlash color="#bfbfbf" />
|
<FaRegEyeSlash color="#bfbfbf" />
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<Center
|
<Center
|
||||||
w="25px"
|
w="25px"
|
||||||
margin="0 1.5%"
|
margin="0 1.5%"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setFieldVisibility({
|
setFieldVisibility({
|
||||||
...fieldVisibility,
|
...fieldVisibility,
|
||||||
[HiddenInputType.OLD_ADMIN_SECRET]: true,
|
[HiddenInputType.OLD_ADMIN_SECRET]: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FaRegEye color="#bfbfbf" />
|
<FaRegEye color="#bfbfbf" />
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
paddingBottom="3%"
|
paddingBottom="3%"
|
||||||
direction={isNotSmallerScreen ? 'row' : 'column'}
|
direction={isNotSmallerScreen ? "row" : "column"}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '50%'}
|
w={isNotSmallerScreen ? "30%" : "50%"}
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm">New Admin Secret:</Text>
|
<Text fontSize="sm">New Admin Secret:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '70%' : '100%'}
|
w={isNotSmallerScreen ? "70%" : "100%"}
|
||||||
mt={isNotSmallerScreen ? '0' : '3'}
|
mt={isNotSmallerScreen ? "0" : "3"}
|
||||||
>
|
>
|
||||||
<InputField
|
<InputField
|
||||||
borderRadius={5}
|
borderRadius={5}
|
||||||
mb={3}
|
mb={3}
|
||||||
variables={variables}
|
variables={variables}
|
||||||
setVariables={setVariables}
|
setVariables={setVariables}
|
||||||
inputType={HiddenInputType.ADMIN_SECRET}
|
inputType={HiddenInputType.ADMIN_SECRET}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
isDisabled={adminSecret.disableInputField}
|
isDisabled={adminSecret.disableInputField}
|
||||||
placeholder="Enter New Admin Secret"
|
placeholder="Enter New Admin Secret"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SecurityAdminSecret;
|
export default SecurityAdminSecret;
|
|
@ -73,7 +73,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
|
||||||
title: 'Error occurred generating jwt keys',
|
title: 'Error occurred generating jwt keys',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
closeHandler();
|
closeHandler();
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,7 +107,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
|
||||||
title: 'Error occurred setting jwt keys',
|
title: 'Error occurred setting jwt keys',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -116,7 +116,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
|
||||||
title: 'JWT keys updated successfully',
|
title: 'JWT keys updated successfully',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
closeHandler();
|
closeHandler();
|
||||||
};
|
};
|
||||||
|
@ -167,7 +167,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{Object.values(HMACEncryptionType).includes(
|
{Object.values(HMACEncryptionType).includes(
|
||||||
stateVariables.JWT_TYPE,
|
stateVariables.JWT_TYPE
|
||||||
) ? (
|
) ? (
|
||||||
<Flex marginTop="8">
|
<Flex marginTop="8">
|
||||||
<Flex w="23%" justifyContent="start" alignItems="center">
|
<Flex w="23%" justifyContent="start" alignItems="center">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
|
@ -12,13 +12,8 @@ import {
|
||||||
Select,
|
Select,
|
||||||
Textarea,
|
Textarea,
|
||||||
Switch,
|
Switch,
|
||||||
|
Code,
|
||||||
Text,
|
Text,
|
||||||
MenuButton,
|
|
||||||
MenuList,
|
|
||||||
MenuItemOption,
|
|
||||||
MenuOptionGroup,
|
|
||||||
Button,
|
|
||||||
Menu,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaRegClone,
|
FaRegClone,
|
||||||
|
@ -26,7 +21,6 @@ import {
|
||||||
FaRegEyeSlash,
|
FaRegEyeSlash,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaTimes,
|
FaTimes,
|
||||||
FaAngleDown,
|
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import {
|
import {
|
||||||
ArrayInputOperations,
|
ArrayInputOperations,
|
||||||
|
@ -37,7 +31,6 @@ import {
|
||||||
TextAreaInputType,
|
TextAreaInputType,
|
||||||
SwitchInputType,
|
SwitchInputType,
|
||||||
DateInputType,
|
DateInputType,
|
||||||
MultiSelectInputType,
|
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { copyTextToClipboard } from '../utils';
|
import { copyTextToClipboard } from '../utils';
|
||||||
|
|
||||||
|
@ -47,18 +40,13 @@ const InputField = ({
|
||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
availableRoles,
|
|
||||||
// This prop is added to improve the user experience for the boolean ENV variable having `DISABLE_` prefix, as those values need to be considered in inverted form.
|
|
||||||
hasReversedValue,
|
|
||||||
...downshiftProps
|
...downshiftProps
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const props = {
|
const props = {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
...downshiftProps,
|
...downshiftProps,
|
||||||
};
|
};
|
||||||
const [availableUserRoles, setAvailableUserRoles] =
|
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
|
||||||
useState<string[]>(availableRoles);
|
|
||||||
const [inputFieldVisibility, setInputFieldVisibility] = useState<
|
|
||||||
Record<string, boolean>
|
Record<string, boolean>
|
||||||
>({
|
>({
|
||||||
ROLES: false,
|
ROLES: false,
|
||||||
|
@ -67,7 +55,7 @@ const InputField = ({
|
||||||
ALLOWED_ORIGINS: false,
|
ALLOWED_ORIGINS: false,
|
||||||
roles: false,
|
roles: false,
|
||||||
});
|
});
|
||||||
const [inputData, setInputData] = useState<Record<string, string>>({
|
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
||||||
ROLES: '',
|
ROLES: '',
|
||||||
DEFAULT_ROLES: '',
|
DEFAULT_ROLES: '',
|
||||||
PROTECTED_ROLES: '',
|
PROTECTED_ROLES: '',
|
||||||
|
@ -77,7 +65,7 @@ const InputField = ({
|
||||||
const updateInputHandler = (
|
const updateInputHandler = (
|
||||||
type: string,
|
type: string,
|
||||||
operation: any,
|
operation: any,
|
||||||
role: string = '',
|
role: string = ''
|
||||||
) => {
|
) => {
|
||||||
if (operation === ArrayInputOperations.APPEND) {
|
if (operation === ArrayInputOperations.APPEND) {
|
||||||
if (inputData[type] !== '') {
|
if (inputData[type] !== '') {
|
||||||
|
@ -91,7 +79,7 @@ const InputField = ({
|
||||||
}
|
}
|
||||||
if (operation === ArrayInputOperations.REMOVE) {
|
if (operation === ArrayInputOperations.REMOVE) {
|
||||||
let updatedEnvVars = variables[type].filter(
|
let updatedEnvVars = variables[type].filter(
|
||||||
(item: string) => item !== role,
|
(item: string) => item !== role
|
||||||
);
|
);
|
||||||
setVariables({
|
setVariables({
|
||||||
...variables,
|
...variables,
|
||||||
|
@ -108,7 +96,7 @@ const InputField = ({
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
},
|
}
|
||||||
) =>
|
) =>
|
||||||
setVariables({
|
setVariables({
|
||||||
...variables,
|
...variables,
|
||||||
|
@ -129,11 +117,11 @@ const InputField = ({
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
value={variables[inputType] || ''}
|
value={variables[inputType] ?? ''}
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
},
|
}
|
||||||
) =>
|
) =>
|
||||||
setVariables({
|
setVariables({
|
||||||
...variables,
|
...variables,
|
||||||
|
@ -220,7 +208,7 @@ const InputField = ({
|
||||||
updateInputHandler(
|
updateInputHandler(
|
||||||
inputType,
|
inputType,
|
||||||
ArrayInputOperations.REMOVE,
|
ArrayInputOperations.REMOVE,
|
||||||
role,
|
role
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -234,7 +222,7 @@ const InputField = ({
|
||||||
size="xs"
|
size="xs"
|
||||||
minW="150px"
|
minW="150px"
|
||||||
placeholder="add a new value"
|
placeholder="add a new value"
|
||||||
value={inputData[inputType] || ''}
|
value={inputData[inputType] ?? ''}
|
||||||
onChange={(e: any) => {
|
onChange={(e: any) => {
|
||||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
setInputData({ ...inputData, [inputType]: e.target.value });
|
||||||
}}
|
}}
|
||||||
|
@ -291,87 +279,6 @@ const InputField = ({
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (Object.values(MultiSelectInputType).includes(inputType)) {
|
|
||||||
return (
|
|
||||||
<Flex w="100%" style={{ position: 'relative' }}>
|
|
||||||
<Flex
|
|
||||||
border="1px solid #e2e8f0"
|
|
||||||
w="100%"
|
|
||||||
borderRadius="var(--chakra-radii-sm)"
|
|
||||||
p="1% 0 0 2.5%"
|
|
||||||
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
|
|
||||||
overflowY="hidden"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
|
|
||||||
{variables[inputType].map((role: string, index: number) => (
|
|
||||||
<Box key={index} margin="0.5%" role="group">
|
|
||||||
<Tag
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
colorScheme="gray"
|
|
||||||
minW="fit-content"
|
|
||||||
>
|
|
||||||
<TagLabel cursor="default">{role}</TagLabel>
|
|
||||||
<TagRightIcon
|
|
||||||
boxSize="12px"
|
|
||||||
as={FaTimes}
|
|
||||||
display="none"
|
|
||||||
cursor="pointer"
|
|
||||||
_groupHover={{ display: 'block' }}
|
|
||||||
onClick={() =>
|
|
||||||
updateInputHandler(
|
|
||||||
inputType,
|
|
||||||
ArrayInputOperations.REMOVE,
|
|
||||||
role,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tag>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
<Menu matchWidth={true}>
|
|
||||||
<MenuButton px="10px" py="7.5px">
|
|
||||||
<FaAngleDown />
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList
|
|
||||||
position="absolute"
|
|
||||||
top="0"
|
|
||||||
right="0"
|
|
||||||
zIndex="10"
|
|
||||||
maxH="150"
|
|
||||||
overflowX="scroll"
|
|
||||||
>
|
|
||||||
<MenuOptionGroup
|
|
||||||
title={undefined}
|
|
||||||
value={variables[inputType]}
|
|
||||||
type="checkbox"
|
|
||||||
onChange={(values: string[] | string) => {
|
|
||||||
setVariables({
|
|
||||||
...variables,
|
|
||||||
[inputType]: values,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{availableUserRoles.map((role) => {
|
|
||||||
return (
|
|
||||||
<MenuItemOption
|
|
||||||
key={`multiselect-menu-${role}`}
|
|
||||||
value={role}
|
|
||||||
>
|
|
||||||
{role}
|
|
||||||
</MenuItemOption>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</MenuOptionGroup>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (Object.values(TextAreaInputType).includes(inputType)) {
|
if (Object.values(TextAreaInputType).includes(inputType)) {
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
|
@ -382,7 +289,7 @@ const InputField = ({
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
},
|
}
|
||||||
) =>
|
) =>
|
||||||
setVariables({
|
setVariables({
|
||||||
...variables,
|
...variables,
|
||||||
|
@ -400,9 +307,7 @@ const InputField = ({
|
||||||
</Text>
|
</Text>
|
||||||
<Switch
|
<Switch
|
||||||
size="md"
|
size="md"
|
||||||
isChecked={
|
isChecked={variables[inputType]}
|
||||||
hasReversedValue ? !variables[inputType] : variables[inputType]
|
|
||||||
}
|
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setVariables({
|
setVariables({
|
||||||
...variables,
|
...variables,
|
||||||
|
|
|
@ -105,7 +105,7 @@ const InviteMembersModal = ({
|
||||||
title: 'Invites sent successfully!',
|
title: 'Invites sent successfully!',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
updateUserList();
|
updateUserList();
|
||||||
|
@ -117,7 +117,7 @@ const InviteMembersModal = ({
|
||||||
title: error?.message || 'Error occurred, try again!',
|
title: error?.message || 'Error occurred, try again!',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,7 @@ const InviteMembersModal = ({
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
updateEmailListHandler(
|
updateEmailListHandler(
|
||||||
ArrayInputOperations.REMOVE,
|
ArrayInputOperations.REMOVE,
|
||||||
index,
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -31,7 +31,6 @@ import {
|
||||||
FiUsers,
|
FiUsers,
|
||||||
FiChevronDown,
|
FiChevronDown,
|
||||||
FiLink,
|
FiLink,
|
||||||
FiFileText,
|
|
||||||
} from 'react-icons/fi';
|
} from 'react-icons/fi';
|
||||||
import { BiCustomize } from 'react-icons/bi';
|
import { BiCustomize } from 'react-icons/bi';
|
||||||
import { AiOutlineKey } from 'react-icons/ai';
|
import { AiOutlineKey } from 'react-icons/ai';
|
||||||
|
@ -114,7 +113,6 @@ const LinkItems: Array<LinkItemProps> = [
|
||||||
},
|
},
|
||||||
{ name: 'Users', icon: FiUsers, route: '/users' },
|
{ name: 'Users', icon: FiUsers, route: '/users' },
|
||||||
{ name: 'Webhooks', icon: FiLink, route: '/webhooks' },
|
{ name: 'Webhooks', icon: FiLink, route: '/webhooks' },
|
||||||
{ name: 'Email Templates', icon: FiFileText, route: '/email-templates' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
interface SidebarProps extends BoxProps {
|
interface SidebarProps extends BoxProps {
|
||||||
|
@ -218,7 +216,7 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
||||||
</NavItem>{' '}
|
</NavItem>{' '}
|
||||||
</Text>
|
</Text>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
),
|
)
|
||||||
)}
|
)}
|
||||||
<Link
|
<Link
|
||||||
href="/playground"
|
href="/playground"
|
||||||
|
|
|
@ -1,565 +0,0 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
Input,
|
|
||||||
InputGroup,
|
|
||||||
MenuItem,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalFooter,
|
|
||||||
ModalHeader,
|
|
||||||
ModalOverlay,
|
|
||||||
Select,
|
|
||||||
Text,
|
|
||||||
useDisclosure,
|
|
||||||
useToast,
|
|
||||||
Alert,
|
|
||||||
AlertIcon,
|
|
||||||
Collapse,
|
|
||||||
Box,
|
|
||||||
TableContainer,
|
|
||||||
Table,
|
|
||||||
Thead,
|
|
||||||
Tr,
|
|
||||||
Th,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Code,
|
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
Stack,
|
|
||||||
Textarea,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
|
||||||
import { useClient } from 'urql';
|
|
||||||
import EmailEditor from 'react-email-editor';
|
|
||||||
import {
|
|
||||||
UpdateModalViews,
|
|
||||||
EmailTemplateInputDataFields,
|
|
||||||
emailTemplateEventNames,
|
|
||||||
emailTemplateVariables,
|
|
||||||
EmailTemplateEditors,
|
|
||||||
} from '../constants';
|
|
||||||
import { capitalizeFirstLetter } from '../utils';
|
|
||||||
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
|
|
||||||
|
|
||||||
interface selectedEmailTemplateDataTypes {
|
|
||||||
[EmailTemplateInputDataFields.ID]: string;
|
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
|
||||||
[EmailTemplateInputDataFields.CREATED_AT]: number;
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateEmailTemplateInputPropTypes {
|
|
||||||
view: UpdateModalViews;
|
|
||||||
selectedTemplate?: selectedEmailTemplateDataTypes;
|
|
||||||
fetchEmailTemplatesData: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface templateVariableDataTypes {
|
|
||||||
text: string;
|
|
||||||
value: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface emailTemplateDataType {
|
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface validatorDataType {
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initTemplateData: emailTemplateDataType = {
|
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: '',
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const initTemplateValidatorData: validatorDataType = {
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const UpdateEmailTemplate = ({
|
|
||||||
view,
|
|
||||||
selectedTemplate,
|
|
||||||
fetchEmailTemplatesData,
|
|
||||||
}: UpdateEmailTemplateInputPropTypes) => {
|
|
||||||
const client = useClient();
|
|
||||||
const toast = useToast();
|
|
||||||
const emailEditorRef = useRef(null);
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [editor, setEditor] = useState<string>(
|
|
||||||
EmailTemplateEditors.PLAIN_HTML_EDITOR,
|
|
||||||
);
|
|
||||||
const [templateVariables, setTemplateVariables] = useState<
|
|
||||||
templateVariableDataTypes[]
|
|
||||||
>([]);
|
|
||||||
const [templateData, setTemplateData] = useState<emailTemplateDataType>({
|
|
||||||
...initTemplateData,
|
|
||||||
});
|
|
||||||
const [validator, setValidator] = useState<validatorDataType>({
|
|
||||||
...initTemplateValidatorData,
|
|
||||||
});
|
|
||||||
const [isDynamicVariableInfoOpen, setIsDynamicVariableInfoOpen] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
|
|
||||||
const onReady = () => {
|
|
||||||
if (selectedTemplate) {
|
|
||||||
const { design } = selectedTemplate;
|
|
||||||
try {
|
|
||||||
if (design) {
|
|
||||||
const designData = JSON.parse(design);
|
|
||||||
// @ts-ignore
|
|
||||||
emailEditorRef.current.editor.loadDesign(designData);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const inputChangehandler = (inputType: string, value: any) => {
|
|
||||||
if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) {
|
|
||||||
setValidator({
|
|
||||||
...validator,
|
|
||||||
[inputType]: value?.trim().length,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTemplateData({ ...templateData, [inputType]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateData = () => {
|
|
||||||
return (
|
|
||||||
!loading &&
|
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 &&
|
|
||||||
templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 &&
|
|
||||||
validator[EmailTemplateInputDataFields.SUBJECT]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateTemplate = async (params: emailTemplateDataType) => {
|
|
||||||
let res: any = {};
|
|
||||||
if (
|
|
||||||
view === UpdateModalViews.Edit &&
|
|
||||||
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
|
||||||
) {
|
|
||||||
res = await client
|
|
||||||
.mutation(EditEmailTemplate, {
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.toPromise();
|
|
||||||
} else {
|
|
||||||
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
res.data?._add_email_template ||
|
|
||||||
res.data?._update_email_template
|
|
||||||
) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(
|
|
||||||
res.data?._add_email_template?.message ||
|
|
||||||
res.data?._update_email_template?.message,
|
|
||||||
),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
setTemplateData({
|
|
||||||
...initTemplateData,
|
|
||||||
});
|
|
||||||
setValidator({ ...initTemplateValidatorData });
|
|
||||||
fetchEmailTemplatesData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveData = async () => {
|
|
||||||
if (!validateData()) return;
|
|
||||||
setLoading(true);
|
|
||||||
let params: emailTemplateDataType = {
|
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]:
|
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]:
|
|
||||||
templateData[EmailTemplateInputDataFields.SUBJECT],
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]:
|
|
||||||
templateData[EmailTemplateInputDataFields.TEMPLATE],
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
};
|
|
||||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
|
||||||
// @ts-ignore
|
|
||||||
await emailEditorRef.current.editor.exportHtml(async (data) => {
|
|
||||||
const { design, html } = data;
|
|
||||||
if (!html || !design) {
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
params = {
|
|
||||||
...params,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
|
||||||
};
|
|
||||||
await updateTemplate(params);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await updateTemplate(params);
|
|
||||||
}
|
|
||||||
view === UpdateModalViews.ADD && onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetData = () => {
|
|
||||||
if (selectedTemplate) {
|
|
||||||
setTemplateData(selectedTemplate);
|
|
||||||
} else {
|
|
||||||
setTemplateData({ ...initTemplateData });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// set template data if edit modal is open
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
isOpen &&
|
|
||||||
view === UpdateModalViews.Edit &&
|
|
||||||
selectedTemplate &&
|
|
||||||
Object.keys(selectedTemplate || {}).length
|
|
||||||
) {
|
|
||||||
const { id, created_at, ...rest } = selectedTemplate;
|
|
||||||
setTemplateData(rest);
|
|
||||||
}
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
// set template variables
|
|
||||||
useEffect(() => {
|
|
||||||
const updatedTemplateVariables = Object.entries(
|
|
||||||
emailTemplateVariables,
|
|
||||||
).reduce((acc, [key, val]): any => {
|
|
||||||
if (
|
|
||||||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
|
|
||||||
emailTemplateEventNames['Verify Otp'] &&
|
|
||||||
val === emailTemplateVariables.otp) ||
|
|
||||||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] ===
|
|
||||||
emailTemplateEventNames['Verify Otp'] &&
|
|
||||||
val === emailTemplateVariables.verification_url)
|
|
||||||
) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...acc,
|
|
||||||
{
|
|
||||||
text: key,
|
|
||||||
value: val.value,
|
|
||||||
description: val.description,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, []);
|
|
||||||
setTemplateVariables(updatedTemplateVariables);
|
|
||||||
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
|
||||||
|
|
||||||
// change editor
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen && selectedTemplate) {
|
|
||||||
const { design } = selectedTemplate;
|
|
||||||
if (design) {
|
|
||||||
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
|
|
||||||
} else {
|
|
||||||
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isOpen, selectedTemplate]);
|
|
||||||
|
|
||||||
// reset fields when editor is changed
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedTemplate?.design) {
|
|
||||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (selectedTemplate?.template) {
|
|
||||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [editor]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{view === UpdateModalViews.ADD ? (
|
|
||||||
<Button
|
|
||||||
leftIcon={<FaPlus />}
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
onClick={onOpen}
|
|
||||||
isDisabled={false}
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
<Center h="100%">Add Template</Center>{' '}
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<MenuItem onClick={onOpen}>Edit</MenuItem>
|
|
||||||
)}
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={() => {
|
|
||||||
resetData();
|
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
size="6xl"
|
|
||||||
>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent>
|
|
||||||
<ModalHeader>
|
|
||||||
{view === UpdateModalViews.ADD
|
|
||||||
? 'Add New Email Template'
|
|
||||||
: 'Edit Email Template'}
|
|
||||||
</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<Flex
|
|
||||||
flexDirection="column"
|
|
||||||
border="1px"
|
|
||||||
borderRadius="md"
|
|
||||||
borderColor="gray.200"
|
|
||||||
p="5"
|
|
||||||
>
|
|
||||||
<Alert
|
|
||||||
status="info"
|
|
||||||
onClick={() =>
|
|
||||||
setIsDynamicVariableInfoOpen(!isDynamicVariableInfoOpen)
|
|
||||||
}
|
|
||||||
borderRadius="5"
|
|
||||||
marginBottom={5}
|
|
||||||
cursor="pointer"
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
<AlertIcon />
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Box width="85%">
|
|
||||||
<b>Note:</b> You can add set of dynamic variables to subject
|
|
||||||
and email body. Click here to see the set of dynamic
|
|
||||||
variables.
|
|
||||||
</Box>
|
|
||||||
{isDynamicVariableInfoOpen ? <FaAngleUp /> : <FaAngleDown />}
|
|
||||||
</Flex>
|
|
||||||
</Alert>
|
|
||||||
<Collapse
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
in={isDynamicVariableInfoOpen}
|
|
||||||
>
|
|
||||||
<TableContainer
|
|
||||||
background="gray.100"
|
|
||||||
borderRadius={5}
|
|
||||||
height={200}
|
|
||||||
width="100%"
|
|
||||||
overflowY="auto"
|
|
||||||
overflowWrap="break-word"
|
|
||||||
>
|
|
||||||
<Table variant="simple">
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th>Variable</Th>
|
|
||||||
<Th>Description</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{templateVariables.map((i) => (
|
|
||||||
<Tr key={i.text}>
|
|
||||||
<Td>
|
|
||||||
<Code fontSize="sm">{`{{.${i.text}}}`}</Code>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Text
|
|
||||||
size="sm"
|
|
||||||
fontSize="sm"
|
|
||||||
overflowWrap="break-word"
|
|
||||||
width="100%"
|
|
||||||
>
|
|
||||||
{i.description}
|
|
||||||
</Text>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</Collapse>
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
marginBottom="2%"
|
|
||||||
>
|
|
||||||
<Flex flex="1">Event Name</Flex>
|
|
||||||
<Flex flex="3">
|
|
||||||
<Select
|
|
||||||
size="md"
|
|
||||||
value={
|
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME]
|
|
||||||
}
|
|
||||||
onChange={(e) =>
|
|
||||||
inputChangehandler(
|
|
||||||
EmailTemplateInputDataFields.EVENT_NAME,
|
|
||||||
e.currentTarget.value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{Object.entries(emailTemplateEventNames).map(
|
|
||||||
([key, value]: any) => (
|
|
||||||
<option value={value} key={key}>
|
|
||||||
{key}
|
|
||||||
</option>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="start"
|
|
||||||
alignItems="center"
|
|
||||||
marginBottom="2%"
|
|
||||||
>
|
|
||||||
<Flex flex="1">Subject</Flex>
|
|
||||||
<Flex flex="3">
|
|
||||||
<InputGroup size="md">
|
|
||||||
<Input
|
|
||||||
pr="4.5rem"
|
|
||||||
type="text"
|
|
||||||
placeholder="Subject Line"
|
|
||||||
value={templateData[EmailTemplateInputDataFields.SUBJECT]}
|
|
||||||
isInvalid={
|
|
||||||
!validator[EmailTemplateInputDataFields.SUBJECT]
|
|
||||||
}
|
|
||||||
onChange={(e) =>
|
|
||||||
inputChangehandler(
|
|
||||||
EmailTemplateInputDataFields.SUBJECT,
|
|
||||||
e.currentTarget.value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="flex-start"
|
|
||||||
alignItems="center"
|
|
||||||
marginBottom="2%"
|
|
||||||
>
|
|
||||||
<Flex flex="1">Template Body</Flex>
|
|
||||||
<Flex flex="3">
|
|
||||||
<RadioGroup
|
|
||||||
onChange={(value) => setEditor(value)}
|
|
||||||
value={editor}
|
|
||||||
>
|
|
||||||
<Stack direction="row" spacing="50px">
|
|
||||||
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
|
|
||||||
Plain HTML
|
|
||||||
</Radio>
|
|
||||||
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
|
|
||||||
Unlayer Editor
|
|
||||||
</Radio>
|
|
||||||
</Stack>
|
|
||||||
</RadioGroup>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="flex-start"
|
|
||||||
alignItems="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="gray.200"
|
|
||||||
>
|
|
||||||
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
|
|
||||||
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
|
||||||
) : (
|
|
||||||
<Textarea
|
|
||||||
value={templateData.template}
|
|
||||||
onChange={(e) => {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
placeholder="Template HTML"
|
|
||||||
border="0"
|
|
||||||
height="500px"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={resetData}
|
|
||||||
isDisabled={loading}
|
|
||||||
marginRight="5"
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
isLoading={loading}
|
|
||||||
onClick={saveData}
|
|
||||||
isDisabled={!validateData()}
|
|
||||||
>
|
|
||||||
<Center h="100%" pt="5%">
|
|
||||||
Save
|
|
||||||
</Center>
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UpdateEmailTemplate;
|
|
|
@ -2,8 +2,6 @@ import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
Code,
|
|
||||||
Collapse,
|
|
||||||
Flex,
|
Flex,
|
||||||
Input,
|
Input,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
|
@ -21,33 +19,20 @@ import {
|
||||||
Text,
|
Text,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
useToast,
|
useToast,
|
||||||
Alert,
|
|
||||||
AlertIcon,
|
|
||||||
Divider,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import { FaMinusCircle, FaPlus } from 'react-icons/fa';
|
||||||
FaAngleDown,
|
|
||||||
FaAngleUp,
|
|
||||||
FaMinusCircle,
|
|
||||||
FaPlus,
|
|
||||||
FaRegClone,
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import { useClient } from 'urql';
|
import { useClient } from 'urql';
|
||||||
import {
|
import {
|
||||||
webhookEventNames,
|
webhookEventNames,
|
||||||
ArrayInputOperations,
|
ArrayInputOperations,
|
||||||
WebhookInputDataFields,
|
WebhookInputDataFields,
|
||||||
WebhookInputHeaderFields,
|
WebhookInputHeaderFields,
|
||||||
UpdateModalViews,
|
UpdateWebhookModalViews,
|
||||||
webhookVerifiedStatus,
|
webhookVerifiedStatus,
|
||||||
webhookPayloadExample,
|
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import {
|
import { capitalizeFirstLetter, validateURI } from '../utils';
|
||||||
capitalizeFirstLetter,
|
|
||||||
copyTextToClipboard,
|
|
||||||
validateURI,
|
|
||||||
} from '../utils';
|
|
||||||
import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation';
|
import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation';
|
||||||
|
import { rest } from 'lodash';
|
||||||
import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi';
|
import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi';
|
||||||
|
|
||||||
interface headersDataType {
|
interface headersDataType {
|
||||||
|
@ -63,14 +48,13 @@ interface headersValidatorDataType {
|
||||||
interface selecetdWebhookDataTypes {
|
interface selecetdWebhookDataTypes {
|
||||||
[WebhookInputDataFields.ID]: string;
|
[WebhookInputDataFields.ID]: string;
|
||||||
[WebhookInputDataFields.EVENT_NAME]: string;
|
[WebhookInputDataFields.EVENT_NAME]: string;
|
||||||
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
|
|
||||||
[WebhookInputDataFields.ENDPOINT]: string;
|
[WebhookInputDataFields.ENDPOINT]: string;
|
||||||
[WebhookInputDataFields.ENABLED]: boolean;
|
[WebhookInputDataFields.ENABLED]: boolean;
|
||||||
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
|
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateWebhookModalInputPropTypes {
|
interface UpdateWebhookModalInputPropTypes {
|
||||||
view: UpdateModalViews;
|
view: UpdateWebhookModalViews;
|
||||||
selectedWebhook?: selecetdWebhookDataTypes;
|
selectedWebhook?: selecetdWebhookDataTypes;
|
||||||
fetchWebookData: Function;
|
fetchWebookData: Function;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +71,6 @@ const initHeadersValidatorData: headersValidatorDataType = {
|
||||||
|
|
||||||
interface webhookDataType {
|
interface webhookDataType {
|
||||||
[WebhookInputDataFields.EVENT_NAME]: string;
|
[WebhookInputDataFields.EVENT_NAME]: string;
|
||||||
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
|
|
||||||
[WebhookInputDataFields.ENDPOINT]: string;
|
[WebhookInputDataFields.ENDPOINT]: string;
|
||||||
[WebhookInputDataFields.ENABLED]: boolean;
|
[WebhookInputDataFields.ENABLED]: boolean;
|
||||||
[WebhookInputDataFields.HEADERS]: headersDataType[];
|
[WebhookInputDataFields.HEADERS]: headersDataType[];
|
||||||
|
@ -99,8 +82,7 @@ interface validatorDataType {
|
||||||
}
|
}
|
||||||
|
|
||||||
const initWebhookData: webhookDataType = {
|
const initWebhookData: webhookDataType = {
|
||||||
[WebhookInputDataFields.EVENT_NAME]: webhookEventNames['User login'],
|
[WebhookInputDataFields.EVENT_NAME]: webhookEventNames.USER_LOGIN,
|
||||||
[WebhookInputDataFields.EVENT_DESCRIPTION]: '',
|
|
||||||
[WebhookInputDataFields.ENDPOINT]: '',
|
[WebhookInputDataFields.ENDPOINT]: '',
|
||||||
[WebhookInputDataFields.ENABLED]: true,
|
[WebhookInputDataFields.ENABLED]: true,
|
||||||
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
|
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
|
||||||
|
@ -121,7 +103,6 @@ const UpdateWebhookModal = ({
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [verifyingEndpoint, setVerifyingEndpoint] = useState<boolean>(false);
|
const [verifyingEndpoint, setVerifyingEndpoint] = useState<boolean>(false);
|
||||||
const [isShowingPayload, setIsShowingPayload] = useState<boolean>(false);
|
|
||||||
const [webhook, setWebhook] = useState<webhookDataType>({
|
const [webhook, setWebhook] = useState<webhookDataType>({
|
||||||
...initWebhookData,
|
...initWebhookData,
|
||||||
});
|
});
|
||||||
|
@ -129,13 +110,13 @@ const UpdateWebhookModal = ({
|
||||||
...initWebhookValidatorData,
|
...initWebhookValidatorData,
|
||||||
});
|
});
|
||||||
const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>(
|
const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>(
|
||||||
webhookVerifiedStatus.PENDING,
|
webhookVerifiedStatus.PENDING
|
||||||
);
|
);
|
||||||
const inputChangehandler = (
|
const inputChangehandler = (
|
||||||
inputType: string,
|
inputType: string,
|
||||||
value: any,
|
value: any,
|
||||||
headerInputType: string = WebhookInputHeaderFields.KEY,
|
headerInputType: string = WebhookInputHeaderFields.KEY,
|
||||||
headerIndex: number = 0,
|
headerIndex: number = 0
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
verifiedStatus !== webhookVerifiedStatus.PENDING &&
|
verifiedStatus !== webhookVerifiedStatus.PENDING &&
|
||||||
|
@ -147,9 +128,6 @@ const UpdateWebhookModal = ({
|
||||||
case WebhookInputDataFields.EVENT_NAME:
|
case WebhookInputDataFields.EVENT_NAME:
|
||||||
setWebhook({ ...webhook, [inputType]: value });
|
setWebhook({ ...webhook, [inputType]: value });
|
||||||
break;
|
break;
|
||||||
case WebhookInputDataFields.EVENT_DESCRIPTION:
|
|
||||||
setWebhook({ ...webhook, [inputType]: value });
|
|
||||||
break;
|
|
||||||
case WebhookInputDataFields.ENDPOINT:
|
case WebhookInputDataFields.ENDPOINT:
|
||||||
setWebhook({ ...webhook, [inputType]: value });
|
setWebhook({ ...webhook, [inputType]: value });
|
||||||
setValidator({
|
setValidator({
|
||||||
|
@ -244,7 +222,7 @@ const UpdateWebhookModal = ({
|
||||||
validator[WebhookInputDataFields.ENDPOINT] &&
|
validator[WebhookInputDataFields.ENDPOINT] &&
|
||||||
!validator[WebhookInputDataFields.HEADERS].some(
|
!validator[WebhookInputDataFields.HEADERS].some(
|
||||||
(headerData: headersValidatorDataType) =>
|
(headerData: headersValidatorDataType) =>
|
||||||
!headerData.key || !headerData.value,
|
!headerData.key || !headerData.value
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -252,8 +230,6 @@ const UpdateWebhookModal = ({
|
||||||
let params: any = {
|
let params: any = {
|
||||||
[WebhookInputDataFields.EVENT_NAME]:
|
[WebhookInputDataFields.EVENT_NAME]:
|
||||||
webhook[WebhookInputDataFields.EVENT_NAME],
|
webhook[WebhookInputDataFields.EVENT_NAME],
|
||||||
[WebhookInputDataFields.EVENT_DESCRIPTION]:
|
|
||||||
webhook[WebhookInputDataFields.EVENT_DESCRIPTION],
|
|
||||||
[WebhookInputDataFields.ENDPOINT]:
|
[WebhookInputDataFields.ENDPOINT]:
|
||||||
webhook[WebhookInputDataFields.ENDPOINT],
|
webhook[WebhookInputDataFields.ENDPOINT],
|
||||||
[WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED],
|
[WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED],
|
||||||
|
@ -264,7 +240,7 @@ const UpdateWebhookModal = ({
|
||||||
(acc, data) => {
|
(acc, data) => {
|
||||||
return data.key ? { ...acc, [data.key]: data.value } : acc;
|
return data.key ? { ...acc, [data.key]: data.value } : acc;
|
||||||
},
|
},
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
if (Object.keys(headers).length) {
|
if (Object.keys(headers).length) {
|
||||||
params[WebhookInputDataFields.HEADERS] = headers;
|
params[WebhookInputDataFields.HEADERS] = headers;
|
||||||
|
@ -278,7 +254,7 @@ const UpdateWebhookModal = ({
|
||||||
const params = getParams();
|
const params = getParams();
|
||||||
let res: any = {};
|
let res: any = {};
|
||||||
if (
|
if (
|
||||||
view === UpdateModalViews.Edit &&
|
view === UpdateWebhookModalViews.Edit &&
|
||||||
selectedWebhook?.[WebhookInputDataFields.ID]
|
selectedWebhook?.[WebhookInputDataFields.ID]
|
||||||
) {
|
) {
|
||||||
res = await client
|
res = await client
|
||||||
|
@ -298,16 +274,16 @@ const UpdateWebhookModal = ({
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
title: capitalizeFirstLetter(res.error.message),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
} else if (res.data?._add_webhook || res.data?._update_webhook) {
|
} else if (res.data?._add_webhook || res.data?._update_webhook) {
|
||||||
toast({
|
toast({
|
||||||
title: capitalizeFirstLetter(
|
title: capitalizeFirstLetter(
|
||||||
res.data?._add_webhook?.message || res.data?._update_webhook?.message,
|
res.data?._add_webhook?.message || res.data?._update_webhook?.message
|
||||||
),
|
),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
setWebhook({
|
setWebhook({
|
||||||
...initWebhookData,
|
...initWebhookData,
|
||||||
|
@ -316,12 +292,12 @@ const UpdateWebhookModal = ({
|
||||||
setValidator({ ...initWebhookValidatorData });
|
setValidator({ ...initWebhookValidatorData });
|
||||||
fetchWebookData();
|
fetchWebookData();
|
||||||
}
|
}
|
||||||
view === UpdateModalViews.ADD && onClose();
|
view === UpdateWebhookModalViews.ADD && onClose();
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
isOpen &&
|
isOpen &&
|
||||||
view === UpdateModalViews.Edit &&
|
view === UpdateWebhookModalViews.Edit &&
|
||||||
selectedWebhook &&
|
selectedWebhook &&
|
||||||
Object.keys(selectedWebhook || {}).length
|
Object.keys(selectedWebhook || {}).length
|
||||||
) {
|
) {
|
||||||
|
@ -341,7 +317,7 @@ const UpdateWebhookModal = ({
|
||||||
setValidator({
|
setValidator({
|
||||||
...validator,
|
...validator,
|
||||||
[WebhookInputDataFields.HEADERS]: new Array(
|
[WebhookInputDataFields.HEADERS]: new Array(
|
||||||
formattedHeadersData.length,
|
formattedHeadersData.length
|
||||||
)
|
)
|
||||||
.fill({})
|
.fill({})
|
||||||
.map(() => ({ ...initHeadersValidatorData })),
|
.map(() => ({ ...initHeadersValidatorData })),
|
||||||
|
@ -371,7 +347,7 @@ const UpdateWebhookModal = ({
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{view === UpdateModalViews.ADD ? (
|
{view === UpdateWebhookModalViews.ADD ? (
|
||||||
<Button
|
<Button
|
||||||
leftIcon={<FaPlus />}
|
leftIcon={<FaPlus />}
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
|
@ -389,7 +365,9 @@ const UpdateWebhookModal = ({
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
{view === UpdateModalViews.ADD ? 'Add New Webhook' : 'Edit Webhook'}
|
{view === UpdateWebhookModalViews.ADD
|
||||||
|
? 'Add New Webhook'
|
||||||
|
: 'Edit Webhook'}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalCloseButton />
|
<ModalCloseButton />
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
@ -410,13 +388,11 @@ const UpdateWebhookModal = ({
|
||||||
<Flex flex="3">
|
<Flex flex="3">
|
||||||
<Select
|
<Select
|
||||||
size="md"
|
size="md"
|
||||||
value={
|
value={webhook[WebhookInputDataFields.EVENT_NAME]}
|
||||||
webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]
|
|
||||||
}
|
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
inputChangehandler(
|
inputChangehandler(
|
||||||
WebhookInputDataFields.EVENT_NAME,
|
WebhookInputDataFields.EVENT_NAME,
|
||||||
e.currentTarget.value,
|
e.currentTarget.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -425,35 +401,11 @@ const UpdateWebhookModal = ({
|
||||||
<option value={value} key={key}>
|
<option value={value} key={key}>
|
||||||
{key}
|
{key}
|
||||||
</option>
|
</option>
|
||||||
),
|
)
|
||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="start"
|
|
||||||
alignItems="center"
|
|
||||||
marginBottom="5%"
|
|
||||||
>
|
|
||||||
<Flex flex="1">Event Description</Flex>
|
|
||||||
<Flex flex="3">
|
|
||||||
<InputGroup size="md">
|
|
||||||
<Input
|
|
||||||
pr="4.5rem"
|
|
||||||
type="text"
|
|
||||||
placeholder="User event"
|
|
||||||
value={webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
|
|
||||||
onChange={(e) =>
|
|
||||||
inputChangehandler(
|
|
||||||
WebhookInputDataFields.EVENT_DESCRIPTION,
|
|
||||||
e.currentTarget.value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
|
@ -472,7 +424,7 @@ const UpdateWebhookModal = ({
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
inputChangehandler(
|
inputChangehandler(
|
||||||
WebhookInputDataFields.ENDPOINT,
|
WebhookInputDataFields.ENDPOINT,
|
||||||
e.currentTarget.value,
|
e.currentTarget.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -496,7 +448,7 @@ const UpdateWebhookModal = ({
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
inputChangehandler(
|
inputChangehandler(
|
||||||
WebhookInputDataFields.ENABLED,
|
WebhookInputDataFields.ENABLED,
|
||||||
!webhook[WebhookInputDataFields.ENABLED],
|
!webhook[WebhookInputDataFields.ENABLED]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -505,12 +457,11 @@ const UpdateWebhookModal = ({
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom="5%"
|
marginBottom="2%"
|
||||||
>
|
>
|
||||||
<Flex>Headers</Flex>
|
<Flex>Headers</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
|
@ -527,8 +478,7 @@ const UpdateWebhookModal = ({
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Flex flexDirection="column" maxH={220} overflowY="scroll">
|
||||||
<Flex flexDirection="column" maxH={220} overflowY="auto">
|
|
||||||
{webhook[WebhookInputDataFields.HEADERS]?.map(
|
{webhook[WebhookInputDataFields.HEADERS]?.map(
|
||||||
(headerData, index) => (
|
(headerData, index) => (
|
||||||
<Flex
|
<Flex
|
||||||
|
@ -551,7 +501,7 @@ const UpdateWebhookModal = ({
|
||||||
WebhookInputDataFields.HEADERS,
|
WebhookInputDataFields.HEADERS,
|
||||||
e.target.value,
|
e.target.value,
|
||||||
WebhookInputHeaderFields.KEY,
|
WebhookInputHeaderFields.KEY,
|
||||||
index,
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
width="30%"
|
width="30%"
|
||||||
|
@ -574,7 +524,7 @@ const UpdateWebhookModal = ({
|
||||||
WebhookInputDataFields.HEADERS,
|
WebhookInputDataFields.HEADERS,
|
||||||
e.target.value,
|
e.target.value,
|
||||||
WebhookInputHeaderFields.VALUE,
|
WebhookInputHeaderFields.VALUE,
|
||||||
index,
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
width="65%"
|
width="65%"
|
||||||
|
@ -594,57 +544,9 @@ const UpdateWebhookModal = ({
|
||||||
</InputRightElement>
|
</InputRightElement>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
)
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Divider marginY={5} />
|
|
||||||
|
|
||||||
<Alert
|
|
||||||
status="info"
|
|
||||||
onClick={() => setIsShowingPayload(!isShowingPayload)}
|
|
||||||
borderRadius="5"
|
|
||||||
cursor="pointer"
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
<AlertIcon />
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
Checkout the example payload
|
|
||||||
{isShowingPayload ? <FaAngleUp /> : <FaAngleDown />}
|
|
||||||
</Flex>
|
|
||||||
</Alert>
|
|
||||||
<Collapse
|
|
||||||
style={{
|
|
||||||
marginTop: 10,
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
in={isShowingPayload}
|
|
||||||
>
|
|
||||||
<Code
|
|
||||||
width="inherit"
|
|
||||||
borderRadius={5}
|
|
||||||
padding={2}
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<pre style={{ overflow: 'auto' }}>
|
|
||||||
{webhookPayloadExample}
|
|
||||||
</pre>
|
|
||||||
{isShowingPayload && (
|
|
||||||
<Flex
|
|
||||||
position="absolute"
|
|
||||||
top={4}
|
|
||||||
right={4}
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() => copyTextToClipboard(webhookPayloadExample)}
|
|
||||||
>
|
|
||||||
<FaRegClone color="#bfbfbf" />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Code>
|
|
||||||
</Collapse>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|
|
@ -161,15 +161,15 @@ const ViewWebhookLogsModal = ({
|
||||||
<Td>
|
<Td>
|
||||||
<Text fontSize="sm">{`${logData.id.substring(
|
<Text fontSize="sm">{`${logData.id.substring(
|
||||||
0,
|
0,
|
||||||
5,
|
5
|
||||||
)}***${logData.id.substring(
|
)}***${logData.id.substring(
|
||||||
logData.id.length - 5,
|
logData.id.length - 5,
|
||||||
logData.id.length,
|
logData.id.length
|
||||||
)}`}</Text>
|
)}`}</Text>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{dayjs(logData.created_at * 1000).format(
|
{dayjs(logData.created_at * 1000).format(
|
||||||
'MMM DD, YYYY',
|
'MMM DD, YYYY'
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
|
|
|
@ -9,20 +9,12 @@ export const TextInputType = {
|
||||||
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
||||||
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
||||||
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
||||||
DISCORD_CLIENT_ID: 'DISCORD_CLIENT_ID',
|
|
||||||
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
|
|
||||||
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
|
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
|
|
||||||
TWITCH_CLIENT_ID: 'TWITCH_CLIENT_ID',
|
|
||||||
ROBLOX_CLIENT_ID: 'ROBLOX_CLIENT_ID',
|
|
||||||
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
||||||
REDIS_URL: 'REDIS_URL',
|
REDIS_URL: 'REDIS_URL',
|
||||||
SMTP_HOST: 'SMTP_HOST',
|
SMTP_HOST: 'SMTP_HOST',
|
||||||
SMTP_PORT: 'SMTP_PORT',
|
SMTP_PORT: 'SMTP_PORT',
|
||||||
SMTP_USERNAME: 'SMTP_USERNAME',
|
SMTP_USERNAME: 'SMTP_USERNAME',
|
||||||
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
|
|
||||||
SENDER_EMAIL: 'SENDER_EMAIL',
|
SENDER_EMAIL: 'SENDER_EMAIL',
|
||||||
SENDER_NAME: 'SENDER_NAME',
|
|
||||||
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
||||||
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
||||||
DATABASE_NAME: 'DATABASE_NAME',
|
DATABASE_NAME: 'DATABASE_NAME',
|
||||||
|
@ -43,11 +35,6 @@ export const HiddenInputType = {
|
||||||
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
||||||
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
||||||
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
||||||
DISCORD_CLIENT_SECRET: 'DISCORD_CLIENT_SECRET',
|
|
||||||
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
|
|
||||||
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
|
|
||||||
TWITCH_CLIENT_SECRET: 'TWITCH_CLIENT_SECRET',
|
|
||||||
ROBLOX_CLIENT_SECRET: 'ROBLOX_CLIENT_SECRET',
|
|
||||||
JWT_SECRET: 'JWT_SECRET',
|
JWT_SECRET: 'JWT_SECRET',
|
||||||
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
||||||
ADMIN_SECRET: 'ADMIN_SECRET',
|
ADMIN_SECRET: 'ADMIN_SECRET',
|
||||||
|
@ -59,17 +46,12 @@ export const ArrayInputType = {
|
||||||
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
||||||
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
||||||
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
||||||
|
USER_ROLES: 'roles',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectInputType = {
|
export const SelectInputType = {
|
||||||
JWT_TYPE: 'JWT_TYPE',
|
JWT_TYPE: 'JWT_TYPE',
|
||||||
GENDER: 'gender',
|
GENDER: 'gender',
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: 'DEFAULT_AUTHORIZE_RESPONSE_TYPE',
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: 'DEFAULT_AUTHORIZE_RESPONSE_MODE',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MultiSelectInputType = {
|
|
||||||
USER_ROLES: 'roles',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextAreaInputType = {
|
export const TextAreaInputType = {
|
||||||
|
@ -79,21 +61,13 @@ export const TextAreaInputType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SwitchInputType = {
|
export const SwitchInputType = {
|
||||||
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
|
|
||||||
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
|
|
||||||
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
|
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
|
||||||
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
|
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
|
||||||
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
|
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
|
||||||
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
||||||
DISABLE_MOBILE_BASIC_AUTHENTICATION: 'DISABLE_MOBILE_BASIC_AUTHENTICATION',
|
|
||||||
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
||||||
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
||||||
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
|
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
|
|
||||||
DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND',
|
|
||||||
DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN',
|
|
||||||
DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateInputType = {
|
export const DateInputType = {
|
||||||
|
@ -134,17 +108,6 @@ export interface envVarTypes {
|
||||||
LINKEDIN_CLIENT_SECRET: string;
|
LINKEDIN_CLIENT_SECRET: string;
|
||||||
APPLE_CLIENT_ID: string;
|
APPLE_CLIENT_ID: string;
|
||||||
APPLE_CLIENT_SECRET: string;
|
APPLE_CLIENT_SECRET: string;
|
||||||
DISCORD_CLIENT_ID: string;
|
|
||||||
DISCORD_CLIENT_SECRET: string;
|
|
||||||
TWITTER_CLIENT_ID: string;
|
|
||||||
TWITTER_CLIENT_SECRET: string;
|
|
||||||
MICROSOFT_CLIENT_ID: string;
|
|
||||||
MICROSOFT_CLIENT_SECRET: string;
|
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
|
|
||||||
TWITCH_CLIENT_ID: string;
|
|
||||||
TWITCH_CLIENT_SECRET: string;
|
|
||||||
ROBLOX_CLIENT_ID: string;
|
|
||||||
ROBLOX_CLIENT_SECRET: string;
|
|
||||||
ROLES: [string] | [];
|
ROLES: [string] | [];
|
||||||
DEFAULT_ROLES: [string] | [];
|
DEFAULT_ROLES: [string] | [];
|
||||||
PROTECTED_ROLES: [string] | [];
|
PROTECTED_ROLES: [string] | [];
|
||||||
|
@ -158,21 +121,16 @@ export interface envVarTypes {
|
||||||
SMTP_PORT: string;
|
SMTP_PORT: string;
|
||||||
SMTP_USERNAME: string;
|
SMTP_USERNAME: string;
|
||||||
SMTP_PASSWORD: string;
|
SMTP_PASSWORD: string;
|
||||||
SMTP_LOCAL_NAME: string;
|
|
||||||
SENDER_EMAIL: string;
|
SENDER_EMAIL: string;
|
||||||
SENDER_NAME: string;
|
|
||||||
ALLOWED_ORIGINS: [string] | [];
|
ALLOWED_ORIGINS: [string] | [];
|
||||||
ORGANIZATION_NAME: string;
|
ORGANIZATION_NAME: string;
|
||||||
ORGANIZATION_LOGO: string;
|
ORGANIZATION_LOGO: string;
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
|
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
|
||||||
ADMIN_SECRET: string;
|
ADMIN_SECRET: string;
|
||||||
APP_COOKIE_SECURE: boolean;
|
|
||||||
ADMIN_COOKIE_SECURE: boolean;
|
|
||||||
DISABLE_LOGIN_PAGE: boolean;
|
DISABLE_LOGIN_PAGE: boolean;
|
||||||
DISABLE_MAGIC_LINK_LOGIN: boolean;
|
DISABLE_MAGIC_LINK_LOGIN: boolean;
|
||||||
DISABLE_EMAIL_VERIFICATION: boolean;
|
DISABLE_EMAIL_VERIFICATION: boolean;
|
||||||
DISABLE_BASIC_AUTHENTICATION: boolean;
|
DISABLE_BASIC_AUTHENTICATION: boolean;
|
||||||
DISABLE_MOBILE_BASIC_AUTHENTICATION: boolean;
|
|
||||||
DISABLE_SIGN_UP: boolean;
|
DISABLE_SIGN_UP: boolean;
|
||||||
DISABLE_STRONG_PASSWORD: boolean;
|
DISABLE_STRONG_PASSWORD: boolean;
|
||||||
OLD_ADMIN_SECRET: string;
|
OLD_ADMIN_SECRET: string;
|
||||||
|
@ -180,13 +138,6 @@ export interface envVarTypes {
|
||||||
DATABASE_TYPE: string;
|
DATABASE_TYPE: string;
|
||||||
DATABASE_URL: string;
|
DATABASE_URL: string;
|
||||||
ACCESS_TOKEN_EXPIRY_TIME: string;
|
ACCESS_TOKEN_EXPIRY_TIME: string;
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
|
|
||||||
DISABLE_PLAYGROUND: boolean;
|
|
||||||
DISABLE_TOTP_LOGIN: boolean;
|
|
||||||
DISABLE_MAIL_OTP_LOGIN: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const envSubViews = {
|
export const envSubViews = {
|
||||||
|
@ -205,28 +156,18 @@ export const envSubViews = {
|
||||||
|
|
||||||
export enum WebhookInputDataFields {
|
export enum WebhookInputDataFields {
|
||||||
ID = 'id',
|
ID = 'id',
|
||||||
EVENT_DESCRIPTION = 'event_description',
|
|
||||||
EVENT_NAME = 'event_name',
|
EVENT_NAME = 'event_name',
|
||||||
ENDPOINT = 'endpoint',
|
ENDPOINT = 'endpoint',
|
||||||
ENABLED = 'enabled',
|
ENABLED = 'enabled',
|
||||||
HEADERS = 'headers',
|
HEADERS = 'headers',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EmailTemplateInputDataFields {
|
|
||||||
ID = 'id',
|
|
||||||
EVENT_NAME = 'event_name',
|
|
||||||
SUBJECT = 'subject',
|
|
||||||
CREATED_AT = 'created_at',
|
|
||||||
TEMPLATE = 'template',
|
|
||||||
DESIGN = 'design',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum WebhookInputHeaderFields {
|
export enum WebhookInputHeaderFields {
|
||||||
KEY = 'key',
|
KEY = 'key',
|
||||||
VALUE = 'value',
|
VALUE = 'value',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UpdateModalViews {
|
export enum UpdateWebhookModalViews {
|
||||||
ADD = 'add',
|
ADD = 'add',
|
||||||
Edit = 'edit',
|
Edit = 'edit',
|
||||||
}
|
}
|
||||||
|
@ -234,22 +175,12 @@ export enum UpdateModalViews {
|
||||||
export const pageLimits: number[] = [5, 10, 15];
|
export const pageLimits: number[] = [5, 10, 15];
|
||||||
|
|
||||||
export const webhookEventNames = {
|
export const webhookEventNames = {
|
||||||
'User signup': 'user.signup',
|
USER_SIGNUP: 'user.signup',
|
||||||
'User created': 'user.created',
|
USER_CREATED: 'user.created',
|
||||||
'User login': 'user.login',
|
USER_LOGIN: 'user.login',
|
||||||
'User deleted': 'user.deleted',
|
USER_DELETED: 'user.deleted',
|
||||||
'User access enabled': 'user.access_enabled',
|
USER_ACCESS_ENABLED: 'user.access_enabled',
|
||||||
'User access revoked': 'user.access_revoked',
|
USER_ACCESS_REVOKED: 'user.access_revoked',
|
||||||
'User deactivated': 'user.deactivated',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const emailTemplateEventNames = {
|
|
||||||
Signup: 'basic_auth_signup',
|
|
||||||
'Magic Link Login': 'magic_link_login',
|
|
||||||
'Update Email': 'update_email',
|
|
||||||
'Forgot Password': 'forgot_password',
|
|
||||||
'Verify Otp': 'verify_otp',
|
|
||||||
'Invite member': 'invite_member',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum webhookVerifiedStatus {
|
export enum webhookVerifiedStatus {
|
||||||
|
@ -257,135 +188,3 @@ export enum webhookVerifiedStatus {
|
||||||
NOT_VERIFIED = 'not_verified',
|
NOT_VERIFIED = 'not_verified',
|
||||||
PENDING = 'verification_pending',
|
PENDING = 'verification_pending',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const emailTemplateVariables = {
|
|
||||||
'user.id': {
|
|
||||||
description: `User identifier`,
|
|
||||||
value: '{.user.id}}',
|
|
||||||
},
|
|
||||||
'user.email': {
|
|
||||||
description: 'User email address',
|
|
||||||
value: '{.user.email}}',
|
|
||||||
},
|
|
||||||
'user.given_name': {
|
|
||||||
description: `User first name`,
|
|
||||||
value: '{.user.given_name}}',
|
|
||||||
},
|
|
||||||
'user.family_name': {
|
|
||||||
description: `User last name`,
|
|
||||||
value: '{.user.family_name}}',
|
|
||||||
},
|
|
||||||
'user.middle_name': {
|
|
||||||
description: `Middle name of user`,
|
|
||||||
value: '{.user.middle_name}}',
|
|
||||||
},
|
|
||||||
'user.nickname': {
|
|
||||||
description: `Nick name of user`,
|
|
||||||
value: '{.user.nickname}}',
|
|
||||||
},
|
|
||||||
'user.preferred_username': {
|
|
||||||
description: `Username, by default it is email`,
|
|
||||||
value: '{.user.preferred_username}}',
|
|
||||||
},
|
|
||||||
'user.signup_methods': {
|
|
||||||
description: `Comma separated list of methods using which user has signed up`,
|
|
||||||
value: '{.user.signup_methods}}',
|
|
||||||
},
|
|
||||||
'user.email_verified': {
|
|
||||||
description: `Whether email is verified or not`,
|
|
||||||
value: '{.user.email_verified}}',
|
|
||||||
},
|
|
||||||
'user.picture': {
|
|
||||||
description: `URL of the user profile picture`,
|
|
||||||
value: '{.user.picture}}',
|
|
||||||
},
|
|
||||||
'user.roles': {
|
|
||||||
description: `Comma separated list of roles assigned to user`,
|
|
||||||
value: '{.user.roles}}',
|
|
||||||
},
|
|
||||||
'user.gender': {
|
|
||||||
description: `Gender of user`,
|
|
||||||
value: '{.user.gender}}',
|
|
||||||
},
|
|
||||||
'user.birthdate': {
|
|
||||||
description: `BirthDate of user`,
|
|
||||||
value: '{.user.birthdate}}',
|
|
||||||
},
|
|
||||||
'user.phone_number': {
|
|
||||||
description: `Phone number of user`,
|
|
||||||
value: '{.user.phone_number}}',
|
|
||||||
},
|
|
||||||
'user.phone_number_verified': {
|
|
||||||
description: `Whether phone number is verified or not`,
|
|
||||||
value: '{.user.phone_number_verified}}',
|
|
||||||
},
|
|
||||||
'user.created_at': {
|
|
||||||
description: `User created at time`,
|
|
||||||
value: '{.user.created_at}}',
|
|
||||||
},
|
|
||||||
'user.updated_at': {
|
|
||||||
description: `Last updated time at user`,
|
|
||||||
value: '{.user.updated_at}}',
|
|
||||||
},
|
|
||||||
'organization.name': {
|
|
||||||
description: `Organization name`,
|
|
||||||
value: '{.organization.name}}',
|
|
||||||
},
|
|
||||||
'organization.logo': {
|
|
||||||
description: `Organization logo`,
|
|
||||||
value: '{.organization.logo}}',
|
|
||||||
},
|
|
||||||
verification_url: {
|
|
||||||
description: `Verification URL in case of events other than verify otp`,
|
|
||||||
value: '{.verification_url}}',
|
|
||||||
},
|
|
||||||
otp: {
|
|
||||||
description: `OTP sent during login with Multi factor authentication`,
|
|
||||||
value: '{.otp}}',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const webhookPayloadExample: string = `{
|
|
||||||
"event_name":"user.login",
|
|
||||||
"user":{
|
|
||||||
"birthdate":null,
|
|
||||||
"created_at":1657524721,
|
|
||||||
"email":"lakhan.m.samani@gmail.com",
|
|
||||||
"email_verified":true,
|
|
||||||
"family_name":"Samani",
|
|
||||||
"gender":null,
|
|
||||||
"given_name":"Lakhan",
|
|
||||||
"id":"466d0b31-1b87-420e-bea5-09d05d79c586",
|
|
||||||
"middle_name":null,
|
|
||||||
"nickname":null,
|
|
||||||
"phone_number":null,
|
|
||||||
"phone_number_verified":false,
|
|
||||||
"picture":"https://lh3.googleusercontent.com/a-/AFdZucppvU6a2zIDkX0wvhhapVjT0ZMKDlYCkQDi3NxcUg=s96-c",
|
|
||||||
"preferred_username":"lakhan.m.samani@gmail.com",
|
|
||||||
"revoked_timestamp":null,
|
|
||||||
"roles":[
|
|
||||||
"user"
|
|
||||||
],
|
|
||||||
"signup_methods":"google",
|
|
||||||
"updated_at":1657526492
|
|
||||||
},
|
|
||||||
"auth_recipe":"google"
|
|
||||||
}`;
|
|
||||||
|
|
||||||
export enum EmailTemplateEditors {
|
|
||||||
UNLAYER_EDITOR = 'unlayer_editor',
|
|
||||||
PLAIN_HTML_EDITOR = 'plain_html_editor',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ResponseTypes = {
|
|
||||||
token: 'token',
|
|
||||||
code: 'code',
|
|
||||||
id_token: 'id_token',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ResponseModes = {
|
|
||||||
query: 'query',
|
|
||||||
form_post: 'form_post',
|
|
||||||
fragment: 'fragment',
|
|
||||||
web_message: 'web_message',
|
|
||||||
};
|
|
||||||
|
|
|
@ -112,27 +112,3 @@ export const TestEndpoint = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const AddEmailTemplate = `
|
|
||||||
mutation addEmailTemplate($params: AddEmailTemplateRequest!) {
|
|
||||||
_add_email_template(params: $params) {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const EditEmailTemplate = `
|
|
||||||
mutation editEmailTemplate($params: UpdateEmailTemplateRequest!) {
|
|
||||||
_update_email_template(params: $params) {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const DeleteEmailTemplate = `
|
|
||||||
mutation deleteEmailTemplate($params: DeleteEmailTemplateRequest!) {
|
|
||||||
_delete_email_template(params: $params) {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
|
@ -18,71 +18,48 @@ export const AdminSessionQuery = `
|
||||||
export const EnvVariablesQuery = `
|
export const EnvVariablesQuery = `
|
||||||
query {
|
query {
|
||||||
_env{
|
_env{
|
||||||
CLIENT_ID
|
CLIENT_ID,
|
||||||
CLIENT_SECRET
|
CLIENT_SECRET,
|
||||||
GOOGLE_CLIENT_ID
|
GOOGLE_CLIENT_ID,
|
||||||
GOOGLE_CLIENT_SECRET
|
GOOGLE_CLIENT_SECRET,
|
||||||
GITHUB_CLIENT_ID
|
GITHUB_CLIENT_ID,
|
||||||
GITHUB_CLIENT_SECRET
|
GITHUB_CLIENT_SECRET,
|
||||||
FACEBOOK_CLIENT_ID
|
FACEBOOK_CLIENT_ID,
|
||||||
FACEBOOK_CLIENT_SECRET
|
FACEBOOK_CLIENT_SECRET,
|
||||||
LINKEDIN_CLIENT_ID
|
LINKEDIN_CLIENT_ID,
|
||||||
LINKEDIN_CLIENT_SECRET
|
LINKEDIN_CLIENT_SECRET,
|
||||||
APPLE_CLIENT_ID
|
APPLE_CLIENT_ID,
|
||||||
APPLE_CLIENT_SECRET
|
APPLE_CLIENT_SECRET,
|
||||||
DISCORD_CLIENT_ID
|
DEFAULT_ROLES,
|
||||||
DISCORD_CLIENT_SECRET
|
PROTECTED_ROLES,
|
||||||
TWITTER_CLIENT_ID
|
ROLES,
|
||||||
TWITTER_CLIENT_SECRET
|
JWT_TYPE,
|
||||||
MICROSOFT_CLIENT_ID
|
JWT_SECRET,
|
||||||
MICROSOFT_CLIENT_SECRET
|
JWT_ROLE_CLAIM,
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
|
JWT_PRIVATE_KEY,
|
||||||
TWITCH_CLIENT_ID
|
JWT_PUBLIC_KEY,
|
||||||
TWITCH_CLIENT_SECRET
|
REDIS_URL,
|
||||||
ROBLOX_CLIENT_ID
|
SMTP_HOST,
|
||||||
ROBLOX_CLIENT_SECRET
|
SMTP_PORT,
|
||||||
DEFAULT_ROLES
|
SMTP_USERNAME,
|
||||||
PROTECTED_ROLES
|
SMTP_PASSWORD,
|
||||||
ROLES
|
SENDER_EMAIL,
|
||||||
JWT_TYPE
|
ALLOWED_ORIGINS,
|
||||||
JWT_SECRET
|
ORGANIZATION_NAME,
|
||||||
JWT_ROLE_CLAIM
|
ORGANIZATION_LOGO,
|
||||||
JWT_PRIVATE_KEY
|
ADMIN_SECRET,
|
||||||
JWT_PUBLIC_KEY
|
DISABLE_LOGIN_PAGE,
|
||||||
REDIS_URL
|
DISABLE_MAGIC_LINK_LOGIN,
|
||||||
SMTP_HOST
|
DISABLE_EMAIL_VERIFICATION,
|
||||||
SMTP_PORT
|
DISABLE_BASIC_AUTHENTICATION,
|
||||||
SMTP_USERNAME
|
DISABLE_SIGN_UP,
|
||||||
SMTP_PASSWORD
|
DISABLE_STRONG_PASSWORD,
|
||||||
SMTP_LOCAL_NAME
|
DISABLE_REDIS_FOR_ENV,
|
||||||
SENDER_EMAIL
|
CUSTOM_ACCESS_TOKEN_SCRIPT,
|
||||||
SENDER_NAME
|
DATABASE_NAME,
|
||||||
ALLOWED_ORIGINS
|
DATABASE_TYPE,
|
||||||
ORGANIZATION_NAME
|
DATABASE_URL,
|
||||||
ORGANIZATION_LOGO
|
ACCESS_TOKEN_EXPIRY_TIME,
|
||||||
ADMIN_SECRET
|
|
||||||
APP_COOKIE_SECURE
|
|
||||||
ADMIN_COOKIE_SECURE
|
|
||||||
DISABLE_LOGIN_PAGE
|
|
||||||
DISABLE_MAGIC_LINK_LOGIN
|
|
||||||
DISABLE_EMAIL_VERIFICATION
|
|
||||||
DISABLE_BASIC_AUTHENTICATION
|
|
||||||
DISABLE_MOBILE_BASIC_AUTHENTICATION
|
|
||||||
DISABLE_SIGN_UP
|
|
||||||
DISABLE_STRONG_PASSWORD
|
|
||||||
DISABLE_REDIS_FOR_ENV
|
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT
|
|
||||||
DATABASE_NAME
|
|
||||||
DATABASE_TYPE
|
|
||||||
DATABASE_URL
|
|
||||||
ACCESS_TOKEN_EXPIRY_TIME
|
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION
|
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE
|
|
||||||
DISABLE_PLAYGROUND
|
|
||||||
DISABLE_TOTP_LOGIN
|
|
||||||
DISABLE_MAIL_OTP_LOGIN
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -100,7 +77,6 @@ export const UserDetailsQuery = `
|
||||||
id
|
id
|
||||||
email
|
email
|
||||||
email_verified
|
email_verified
|
||||||
phone_number_verified
|
|
||||||
given_name
|
given_name
|
||||||
family_name
|
family_name
|
||||||
middle_name
|
middle_name
|
||||||
|
@ -132,7 +108,6 @@ export const WebhooksDataQuery = `
|
||||||
_webhooks(params: $params){
|
_webhooks(params: $params){
|
||||||
webhooks{
|
webhooks{
|
||||||
id
|
id
|
||||||
event_description
|
|
||||||
event_name
|
event_name
|
||||||
endpoint
|
endpoint
|
||||||
enabled
|
enabled
|
||||||
|
@ -148,27 +123,6 @@ export const WebhooksDataQuery = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const EmailTemplatesQuery = `
|
|
||||||
query getEmailTemplates($params: PaginatedInput!) {
|
|
||||||
_email_templates(params: $params) {
|
|
||||||
email_templates {
|
|
||||||
id
|
|
||||||
event_name
|
|
||||||
subject
|
|
||||||
created_at
|
|
||||||
template
|
|
||||||
design
|
|
||||||
}
|
|
||||||
pagination {
|
|
||||||
limit
|
|
||||||
page
|
|
||||||
offset
|
|
||||||
total
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const WebhookLogsQuery = `
|
export const WebhookLogsQuery = `
|
||||||
query getWebhookLogs($params: ListWebhookLogRequest!) {
|
query getWebhookLogs($params: ListWebhookLogRequest!) {
|
||||||
_webhook_logs(params: $params) {
|
_webhook_logs(params: $params) {
|
||||||
|
@ -188,12 +142,3 @@ export const WebhookLogsQuery = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GetAvailableRolesQuery = `
|
|
||||||
query {
|
|
||||||
_env {
|
|
||||||
ROLES
|
|
||||||
PROTECTED_ROLES
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
|
@ -6,5 +6,5 @@ ReactDOM.render(
|
||||||
<div>
|
<div>
|
||||||
<App />
|
<App />
|
||||||
</div>,
|
</div>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default function Auth() {
|
||||||
title: capitalizeFirstLetter(error.message),
|
title: capitalizeFirstLetter(error.message),
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,348 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { useClient } from 'urql';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
IconButton,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuList,
|
|
||||||
NumberDecrementStepper,
|
|
||||||
NumberIncrementStepper,
|
|
||||||
NumberInput,
|
|
||||||
NumberInputField,
|
|
||||||
NumberInputStepper,
|
|
||||||
Select,
|
|
||||||
Spinner,
|
|
||||||
Table,
|
|
||||||
TableCaption,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Text,
|
|
||||||
Th,
|
|
||||||
Thead,
|
|
||||||
Tooltip,
|
|
||||||
Tr,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
FaAngleDoubleLeft,
|
|
||||||
FaAngleDoubleRight,
|
|
||||||
FaAngleDown,
|
|
||||||
FaAngleLeft,
|
|
||||||
FaAngleRight,
|
|
||||||
FaExclamationCircle,
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import UpdateEmailTemplateModal from '../components/UpdateEmailTemplateModal';
|
|
||||||
import {
|
|
||||||
pageLimits,
|
|
||||||
UpdateModalViews,
|
|
||||||
EmailTemplateInputDataFields,
|
|
||||||
} from '../constants';
|
|
||||||
import { EmailTemplatesQuery } from '../graphql/queries';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import DeleteEmailTemplateModal from '../components/DeleteEmailTemplateModal';
|
|
||||||
|
|
||||||
interface paginationPropTypes {
|
|
||||||
limit: number;
|
|
||||||
page: number;
|
|
||||||
offset: number;
|
|
||||||
total: number;
|
|
||||||
maxPages: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EmailTemplateDataType {
|
|
||||||
[EmailTemplateInputDataFields.ID]: string;
|
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
|
||||||
[EmailTemplateInputDataFields.CREATED_AT]: number;
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EmailTemplates = () => {
|
|
||||||
const client = useClient();
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [emailTemplatesData, setEmailTemplatesData] = useState<
|
|
||||||
EmailTemplateDataType[]
|
|
||||||
>([]);
|
|
||||||
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
|
|
||||||
limit: 5,
|
|
||||||
page: 1,
|
|
||||||
offset: 0,
|
|
||||||
total: 0,
|
|
||||||
maxPages: 1,
|
|
||||||
});
|
|
||||||
const getMaxPages = (pagination: paginationPropTypes) => {
|
|
||||||
const { limit, total } = pagination;
|
|
||||||
if (total > 1) {
|
|
||||||
return total % limit === 0
|
|
||||||
? total / limit
|
|
||||||
: parseInt(`${total / limit}`) + 1;
|
|
||||||
} else return 1;
|
|
||||||
};
|
|
||||||
const fetchEmailTemplatesData = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
const res = await client
|
|
||||||
.query(EmailTemplatesQuery, {
|
|
||||||
params: {
|
|
||||||
pagination: {
|
|
||||||
limit: paginationProps.limit,
|
|
||||||
page: paginationProps.page,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.toPromise();
|
|
||||||
if (res.data?._email_templates) {
|
|
||||||
const { pagination, email_templates: emailTemplates } =
|
|
||||||
res.data?._email_templates;
|
|
||||||
const maxPages = getMaxPages(pagination);
|
|
||||||
if (emailTemplates?.length) {
|
|
||||||
setEmailTemplatesData(emailTemplates);
|
|
||||||
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
|
|
||||||
} else {
|
|
||||||
if (paginationProps.page !== 1) {
|
|
||||||
setPaginationProps({
|
|
||||||
...paginationProps,
|
|
||||||
...pagination,
|
|
||||||
maxPages,
|
|
||||||
page: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
const paginationHandler = (value: Record<string, number>) => {
|
|
||||||
setPaginationProps({ ...paginationProps, ...value });
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
fetchEmailTemplatesData();
|
|
||||||
}, [paginationProps.page, paginationProps.limit]);
|
|
||||||
return (
|
|
||||||
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
|
||||||
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
|
|
||||||
<Text fontSize="md" fontWeight="bold">
|
|
||||||
Email Templates
|
|
||||||
</Text>
|
|
||||||
<UpdateEmailTemplateModal
|
|
||||||
view={UpdateModalViews.ADD}
|
|
||||||
fetchEmailTemplatesData={fetchEmailTemplatesData}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
{!loading ? (
|
|
||||||
emailTemplatesData.length ? (
|
|
||||||
<Table variant="simple">
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th>Event Name</Th>
|
|
||||||
<Th>Subject</Th>
|
|
||||||
<Th>Created At</Th>
|
|
||||||
<Th>Actions</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{emailTemplatesData.map((templateData: EmailTemplateDataType) => (
|
|
||||||
<Tr
|
|
||||||
key={templateData[EmailTemplateInputDataFields.ID]}
|
|
||||||
style={{ fontSize: 14 }}
|
|
||||||
>
|
|
||||||
<Td maxW="300">
|
|
||||||
{templateData[EmailTemplateInputDataFields.EVENT_NAME]}
|
|
||||||
</Td>
|
|
||||||
<Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td>
|
|
||||||
<Td>
|
|
||||||
{dayjs(templateData.created_at * 1000).format(
|
|
||||||
'MMM DD, YYYY',
|
|
||||||
)}
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Menu>
|
|
||||||
<MenuButton as={Button} variant="unstyled" size="sm">
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm" fontWeight="light">
|
|
||||||
Menu
|
|
||||||
</Text>
|
|
||||||
<FaAngleDown style={{ marginLeft: 10 }} />
|
|
||||||
</Flex>
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList>
|
|
||||||
<UpdateEmailTemplateModal
|
|
||||||
view={UpdateModalViews.Edit}
|
|
||||||
selectedTemplate={templateData}
|
|
||||||
fetchEmailTemplatesData={fetchEmailTemplatesData}
|
|
||||||
/>
|
|
||||||
<DeleteEmailTemplateModal
|
|
||||||
emailTemplateId={
|
|
||||||
templateData[EmailTemplateInputDataFields.ID]
|
|
||||||
}
|
|
||||||
eventName={
|
|
||||||
templateData[
|
|
||||||
EmailTemplateInputDataFields.EVENT_NAME
|
|
||||||
]
|
|
||||||
}
|
|
||||||
fetchEmailTemplatesData={fetchEmailTemplatesData}
|
|
||||||
/>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
|
|
||||||
<TableCaption>
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
m="2% 0"
|
|
||||||
>
|
|
||||||
<Flex flex="1">
|
|
||||||
<Tooltip label="First Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={paginationProps.page <= 1}
|
|
||||||
mr={4}
|
|
||||||
icon={<FaAngleDoubleLeft />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Previous Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.page - 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={paginationProps.page <= 1}
|
|
||||||
icon={<FaAngleLeft />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
flex="8"
|
|
||||||
justifyContent="space-evenly"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text mr={8}>
|
|
||||||
Page{' '}
|
|
||||||
<Text fontWeight="bold" as="span">
|
|
||||||
{paginationProps.page}
|
|
||||||
</Text>{' '}
|
|
||||||
of{' '}
|
|
||||||
<Text fontWeight="bold" as="span">
|
|
||||||
{paginationProps.maxPages}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Text flexShrink="0">Go to page:</Text>{' '}
|
|
||||||
<NumberInput
|
|
||||||
ml={2}
|
|
||||||
mr={8}
|
|
||||||
w={28}
|
|
||||||
min={1}
|
|
||||||
max={paginationProps.maxPages}
|
|
||||||
onChange={(value) =>
|
|
||||||
paginationHandler({
|
|
||||||
page: parseInt(value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
value={paginationProps.page}
|
|
||||||
>
|
|
||||||
<NumberInputField />
|
|
||||||
<NumberInputStepper>
|
|
||||||
<NumberIncrementStepper />
|
|
||||||
<NumberDecrementStepper />
|
|
||||||
</NumberInputStepper>
|
|
||||||
</NumberInput>
|
|
||||||
</Flex>
|
|
||||||
<Select
|
|
||||||
w={32}
|
|
||||||
value={paginationProps.limit}
|
|
||||||
onChange={(e) =>
|
|
||||||
paginationHandler({
|
|
||||||
page: 1,
|
|
||||||
limit: parseInt(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{pageLimits.map((pageSize) => (
|
|
||||||
<option key={pageSize} value={pageSize}>
|
|
||||||
Show {pageSize}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Flex>
|
|
||||||
<Flex flex="1">
|
|
||||||
<Tooltip label="Next Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.page + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={
|
|
||||||
paginationProps.page >= paginationProps.maxPages
|
|
||||||
}
|
|
||||||
icon={<FaAngleRight />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Last Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.maxPages,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={
|
|
||||||
paginationProps.page >= paginationProps.maxPages
|
|
||||||
}
|
|
||||||
ml={4}
|
|
||||||
icon={<FaAngleDoubleRight />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</TableCaption>
|
|
||||||
)}
|
|
||||||
</Table>
|
|
||||||
) : (
|
|
||||||
<Flex
|
|
||||||
flexDirection="column"
|
|
||||||
minH="25vh"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Center w="50px" marginRight="1.5%">
|
|
||||||
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
|
|
||||||
</Center>
|
|
||||||
<Text
|
|
||||||
fontSize="2xl"
|
|
||||||
paddingRight="1%"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="#d9d9d9"
|
|
||||||
>
|
|
||||||
No Data
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<Center minH="25vh">
|
|
||||||
<Spinner />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EmailTemplates;
|
|
|
@ -50,17 +50,6 @@ const Environment = () => {
|
||||||
LINKEDIN_CLIENT_SECRET: '',
|
LINKEDIN_CLIENT_SECRET: '',
|
||||||
APPLE_CLIENT_ID: '',
|
APPLE_CLIENT_ID: '',
|
||||||
APPLE_CLIENT_SECRET: '',
|
APPLE_CLIENT_SECRET: '',
|
||||||
DISCORD_CLIENT_ID: '',
|
|
||||||
DISCORD_CLIENT_SECRET: '',
|
|
||||||
TWITTER_CLIENT_ID: '',
|
|
||||||
TWITTER_CLIENT_SECRET: '',
|
|
||||||
MICROSOFT_CLIENT_ID: '',
|
|
||||||
MICROSOFT_CLIENT_SECRET: '',
|
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
|
|
||||||
TWITCH_CLIENT_ID: '',
|
|
||||||
TWITCH_CLIENT_SECRET: '',
|
|
||||||
ROBLOX_CLIENT_ID: '',
|
|
||||||
ROBLOX_CLIENT_SECRET: '',
|
|
||||||
ROLES: [],
|
ROLES: [],
|
||||||
DEFAULT_ROLES: [],
|
DEFAULT_ROLES: [],
|
||||||
PROTECTED_ROLES: [],
|
PROTECTED_ROLES: [],
|
||||||
|
@ -74,21 +63,16 @@ const Environment = () => {
|
||||||
SMTP_PORT: '',
|
SMTP_PORT: '',
|
||||||
SMTP_USERNAME: '',
|
SMTP_USERNAME: '',
|
||||||
SMTP_PASSWORD: '',
|
SMTP_PASSWORD: '',
|
||||||
SMTP_LOCAL_NAME: '',
|
|
||||||
SENDER_EMAIL: '',
|
SENDER_EMAIL: '',
|
||||||
SENDER_NAME: '',
|
|
||||||
ALLOWED_ORIGINS: [],
|
ALLOWED_ORIGINS: [],
|
||||||
ORGANIZATION_NAME: '',
|
ORGANIZATION_NAME: '',
|
||||||
ORGANIZATION_LOGO: '',
|
ORGANIZATION_LOGO: '',
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
|
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
|
||||||
ADMIN_SECRET: '',
|
ADMIN_SECRET: '',
|
||||||
APP_COOKIE_SECURE: false,
|
|
||||||
ADMIN_COOKIE_SECURE: false,
|
|
||||||
DISABLE_LOGIN_PAGE: false,
|
DISABLE_LOGIN_PAGE: false,
|
||||||
DISABLE_MAGIC_LINK_LOGIN: false,
|
DISABLE_MAGIC_LINK_LOGIN: false,
|
||||||
DISABLE_EMAIL_VERIFICATION: false,
|
DISABLE_EMAIL_VERIFICATION: false,
|
||||||
DISABLE_BASIC_AUTHENTICATION: false,
|
DISABLE_BASIC_AUTHENTICATION: false,
|
||||||
DISABLE_MOBILE_BASIC_AUTHENTICATION: false,
|
|
||||||
DISABLE_SIGN_UP: false,
|
DISABLE_SIGN_UP: false,
|
||||||
DISABLE_STRONG_PASSWORD: false,
|
DISABLE_STRONG_PASSWORD: false,
|
||||||
OLD_ADMIN_SECRET: '',
|
OLD_ADMIN_SECRET: '',
|
||||||
|
@ -96,13 +80,6 @@ const Environment = () => {
|
||||||
DATABASE_TYPE: '',
|
DATABASE_TYPE: '',
|
||||||
DATABASE_URL: '',
|
DATABASE_URL: '',
|
||||||
ACCESS_TOKEN_EXPIRY_TIME: '',
|
ACCESS_TOKEN_EXPIRY_TIME: '',
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: false,
|
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
|
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
|
|
||||||
DISABLE_PLAYGROUND: false,
|
|
||||||
DISABLE_TOTP_LOGIN: false,
|
|
||||||
DISABLE_MAIL_OTP_LOGIN: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [fieldVisibility, setFieldVisibility] = React.useState<
|
const [fieldVisibility, setFieldVisibility] = React.useState<
|
||||||
|
@ -113,9 +90,6 @@ const Environment = () => {
|
||||||
FACEBOOK_CLIENT_SECRET: false,
|
FACEBOOK_CLIENT_SECRET: false,
|
||||||
LINKEDIN_CLIENT_SECRET: false,
|
LINKEDIN_CLIENT_SECRET: false,
|
||||||
APPLE_CLIENT_SECRET: false,
|
APPLE_CLIENT_SECRET: false,
|
||||||
DISCORD_CLIENT_SECRET: false,
|
|
||||||
TWITTER_CLIENT_SECRET: false,
|
|
||||||
TWITCH_CLIENT_SECRET: false,
|
|
||||||
JWT_SECRET: false,
|
JWT_SECRET: false,
|
||||||
SMTP_PASSWORD: false,
|
SMTP_PASSWORD: false,
|
||||||
ADMIN_SECRET: false,
|
ADMIN_SECRET: false,
|
||||||
|
@ -176,7 +150,7 @@ const Environment = () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
[property]: envVariables[property],
|
[property]: envVariables[property],
|
||||||
}),
|
}),
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
|
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
|
||||||
|
@ -221,7 +195,7 @@ const Environment = () => {
|
||||||
} variables`,
|
} variables`,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import {
|
||||||
MenuItem,
|
MenuItem,
|
||||||
useToast,
|
useToast,
|
||||||
Spinner,
|
Spinner,
|
||||||
TableContainer,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaAngleLeft,
|
FaAngleLeft,
|
||||||
|
@ -165,25 +164,14 @@ export default function Users() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const userVerificationHandler = async (user: userDataTypes) => {
|
const userVerificationHandler = async (user: userDataTypes) => {
|
||||||
const { id, email, phone_number } = user;
|
const { id, email } = user;
|
||||||
let params = {};
|
|
||||||
if (email) {
|
|
||||||
params = {
|
|
||||||
id,
|
|
||||||
email,
|
|
||||||
email_verified: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (phone_number) {
|
|
||||||
params = {
|
|
||||||
id,
|
|
||||||
phone_number,
|
|
||||||
phone_number_verified: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const res = await client
|
const res = await client
|
||||||
.mutation(UpdateUser, {
|
.mutation(UpdateUser, {
|
||||||
params,
|
params: {
|
||||||
|
id,
|
||||||
|
email,
|
||||||
|
email_verified: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.toPromise();
|
.toPromise();
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
|
@ -191,14 +179,14 @@ export default function Users() {
|
||||||
title: 'User verification failed',
|
title: 'User verification failed',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
} else if (res.data?._update_user?.id) {
|
} else if (res.data?._update_user?.id) {
|
||||||
toast({
|
toast({
|
||||||
title: 'User verification successful',
|
title: 'User verification successful',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateUserList();
|
updateUserList();
|
||||||
|
@ -206,7 +194,7 @@ export default function Users() {
|
||||||
|
|
||||||
const updateAccessHandler = async (
|
const updateAccessHandler = async (
|
||||||
id: string,
|
id: string,
|
||||||
action: updateAccessActions,
|
action: updateAccessActions
|
||||||
) => {
|
) => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case updateAccessActions.ENABLE:
|
case updateAccessActions.ENABLE:
|
||||||
|
@ -222,14 +210,14 @@ export default function Users() {
|
||||||
title: 'User access enable failed',
|
title: 'User access enable failed',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: 'User access enabled successfully',
|
title: 'User access enabled successfully',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateUserList();
|
updateUserList();
|
||||||
|
@ -247,14 +235,14 @@ export default function Users() {
|
||||||
title: 'User access revoke failed',
|
title: 'User access revoke failed',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: 'User access revoked successfully',
|
title: 'User access revoked successfully',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateUserList();
|
updateUserList();
|
||||||
|
@ -279,7 +267,7 @@ export default function Users() {
|
||||||
} for user`,
|
} for user`,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
updateUserList();
|
updateUserList();
|
||||||
return;
|
return;
|
||||||
|
@ -288,7 +276,7 @@ export default function Users() {
|
||||||
title: 'Multi factor authentication update failed for user',
|
title: 'Multi factor authentication update failed for user',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -305,278 +293,256 @@ export default function Users() {
|
||||||
</Flex>
|
</Flex>
|
||||||
{!loading ? (
|
{!loading ? (
|
||||||
userList.length > 0 ? (
|
userList.length > 0 ? (
|
||||||
<TableContainer>
|
<Table variant="simple">
|
||||||
<Table variant="simple">
|
<Thead>
|
||||||
<Thead>
|
<Tr>
|
||||||
<Tr>
|
<Th>Email</Th>
|
||||||
<Th>Email / Phone</Th>
|
<Th>Created At</Th>
|
||||||
<Th>Created At</Th>
|
<Th>Signup Methods</Th>
|
||||||
<Th>Signup Methods</Th>
|
<Th>Roles</Th>
|
||||||
<Th>Roles</Th>
|
<Th>Verified</Th>
|
||||||
<Th>Verified</Th>
|
<Th>Access</Th>
|
||||||
<Th>Access</Th>
|
<Th>MFA</Th>
|
||||||
<Th>
|
<Th>Actions</Th>
|
||||||
<Tooltip label="MultiFactor Authentication Enabled / Disabled">
|
</Tr>
|
||||||
MFA
|
</Thead>
|
||||||
</Tooltip>
|
<Tbody>
|
||||||
</Th>
|
{userList.map((user: userDataTypes) => {
|
||||||
<Th>Actions</Th>
|
const { email_verified, created_at, ...rest }: any = user;
|
||||||
</Tr>
|
return (
|
||||||
</Thead>
|
<Tr key={user.id} style={{ fontSize: 14 }}>
|
||||||
<Tbody>
|
<Td maxW="300">{user.email}</Td>
|
||||||
{userList.map((user: userDataTypes) => {
|
<Td>
|
||||||
const {
|
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
|
||||||
email_verified,
|
</Td>
|
||||||
phone_number_verified,
|
<Td>{user.signup_methods}</Td>
|
||||||
created_at,
|
<Td>{user.roles.join(', ')}</Td>
|
||||||
...rest
|
<Td>
|
||||||
}: any = user;
|
<Tag
|
||||||
return (
|
size="sm"
|
||||||
<Tr key={user.id} style={{ fontSize: 14 }}>
|
variant="outline"
|
||||||
<Td maxW="300">{user.email || user.phone_number}</Td>
|
colorScheme={user.email_verified ? 'green' : 'yellow'}
|
||||||
<Td>
|
>
|
||||||
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
|
{user.email_verified.toString()}
|
||||||
</Td>
|
</Tag>
|
||||||
<Td>{user.signup_methods}</Td>
|
</Td>
|
||||||
<Td>{user.roles.join(', ')}</Td>
|
<Td>
|
||||||
<Td>
|
<Tag
|
||||||
<Tag
|
size="sm"
|
||||||
size="sm"
|
variant="outline"
|
||||||
variant="outline"
|
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
|
||||||
colorScheme={
|
>
|
||||||
user.email_verified || user.phone_number_verified
|
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
|
||||||
? 'green'
|
</Tag>
|
||||||
: 'yellow'
|
</Td>
|
||||||
}
|
<Td>
|
||||||
>
|
<Tag
|
||||||
{(
|
size="sm"
|
||||||
user.email_verified || user.phone_number_verified
|
variant="outline"
|
||||||
).toString()}
|
colorScheme={
|
||||||
</Tag>
|
user.is_multi_factor_auth_enabled ? 'green' : 'red'
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Tag
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
|
|
||||||
>
|
|
||||||
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
|
|
||||||
</Tag>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Tag
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
colorScheme={
|
|
||||||
user.is_multi_factor_auth_enabled ? 'green' : 'red'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{user.is_multi_factor_auth_enabled
|
|
||||||
? 'Enabled'
|
|
||||||
: 'Disabled'}
|
|
||||||
</Tag>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Menu>
|
|
||||||
<MenuButton as={Button} variant="unstyled" size="sm">
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm" fontWeight="light">
|
|
||||||
Menu
|
|
||||||
</Text>
|
|
||||||
<FaAngleDown style={{ marginLeft: 10 }} />
|
|
||||||
</Flex>
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList>
|
|
||||||
{!user.email_verified &&
|
|
||||||
!user.phone_number_verified && (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => userVerificationHandler(user)}
|
|
||||||
>
|
|
||||||
Verify User
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
<EditUserModal
|
|
||||||
user={rest}
|
|
||||||
updateUserList={updateUserList}
|
|
||||||
/>
|
|
||||||
<DeleteUserModal
|
|
||||||
user={rest}
|
|
||||||
updateUserList={updateUserList}
|
|
||||||
/>
|
|
||||||
{user.revoked_timestamp ? (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() =>
|
|
||||||
updateAccessHandler(
|
|
||||||
user.id,
|
|
||||||
updateAccessActions.ENABLE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Enable Access
|
|
||||||
</MenuItem>
|
|
||||||
) : (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() =>
|
|
||||||
updateAccessHandler(
|
|
||||||
user.id,
|
|
||||||
updateAccessActions.REVOKE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Revoke Access
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
{user.is_multi_factor_auth_enabled ? (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() =>
|
|
||||||
multiFactorAuthUpdateHandler(user)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Disable MultiFactor Authentication
|
|
||||||
</MenuItem>
|
|
||||||
) : (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() =>
|
|
||||||
multiFactorAuthUpdateHandler(user)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Enable MultiFactor Authentication
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tbody>
|
|
||||||
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
|
|
||||||
<TableCaption>
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
m="2% 0"
|
|
||||||
>
|
|
||||||
<Flex flex="1">
|
|
||||||
<Tooltip label="First Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={paginationProps.page <= 1}
|
|
||||||
mr={4}
|
|
||||||
icon={<FaAngleDoubleLeft />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Previous Page">
|
|
||||||
<IconButton
|
|
||||||
aria-label="icon button"
|
|
||||||
onClick={() =>
|
|
||||||
paginationHandler({
|
|
||||||
page: paginationProps.page - 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDisabled={paginationProps.page <= 1}
|
|
||||||
icon={<FaAngleLeft />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
flex="8"
|
|
||||||
justifyContent="space-evenly"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Text mr={8}>
|
|
||||||
Page{' '}
|
|
||||||
<Text fontWeight="bold" as="span">
|
|
||||||
{paginationProps.page}
|
|
||||||
</Text>{' '}
|
|
||||||
of{' '}
|
|
||||||
<Text fontWeight="bold" as="span">
|
|
||||||
{paginationProps.maxPages}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Text flexShrink="0">Go to page:</Text>{' '}
|
|
||||||
<NumberInput
|
|
||||||
ml={2}
|
|
||||||
mr={8}
|
|
||||||
w={28}
|
|
||||||
min={1}
|
|
||||||
max={paginationProps.maxPages}
|
|
||||||
onChange={(value) =>
|
|
||||||
paginationHandler({
|
|
||||||
page: parseInt(value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
value={paginationProps.page}
|
|
||||||
>
|
|
||||||
<NumberInputField />
|
|
||||||
<NumberInputStepper>
|
|
||||||
<NumberIncrementStepper />
|
|
||||||
<NumberDecrementStepper />
|
|
||||||
</NumberInputStepper>
|
|
||||||
</NumberInput>
|
|
||||||
</Flex>
|
|
||||||
<Select
|
|
||||||
w={32}
|
|
||||||
value={paginationProps.limit}
|
|
||||||
onChange={(e) =>
|
|
||||||
paginationHandler({
|
|
||||||
page: 1,
|
|
||||||
limit: parseInt(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{getLimits(paginationProps).map((pageSize) => (
|
{user.is_multi_factor_auth_enabled
|
||||||
<option key={pageSize} value={pageSize}>
|
? 'Enabled'
|
||||||
Show {pageSize}
|
: 'Disabled'}
|
||||||
</option>
|
</Tag>
|
||||||
))}
|
</Td>
|
||||||
</Select>
|
<Td>
|
||||||
</Flex>
|
<Menu>
|
||||||
<Flex flex="1">
|
<MenuButton as={Button} variant="unstyled" size="sm">
|
||||||
<Tooltip label="Next Page">
|
<Flex
|
||||||
<IconButton
|
justifyContent="space-between"
|
||||||
aria-label="icon button"
|
alignItems="center"
|
||||||
onClick={() =>
|
>
|
||||||
paginationHandler({
|
<Text fontSize="sm" fontWeight="light">
|
||||||
page: paginationProps.page + 1,
|
Menu
|
||||||
})
|
</Text>
|
||||||
}
|
<FaAngleDown style={{ marginLeft: 10 }} />
|
||||||
isDisabled={
|
</Flex>
|
||||||
paginationProps.page >= paginationProps.maxPages
|
</MenuButton>
|
||||||
}
|
<MenuList>
|
||||||
icon={<FaAngleRight />}
|
{!user.email_verified && (
|
||||||
/>
|
<MenuItem
|
||||||
</Tooltip>
|
onClick={() => userVerificationHandler(user)}
|
||||||
<Tooltip label="Last Page">
|
>
|
||||||
<IconButton
|
Verify User
|
||||||
aria-label="icon button"
|
</MenuItem>
|
||||||
onClick={() =>
|
)}
|
||||||
paginationHandler({
|
<EditUserModal
|
||||||
page: paginationProps.maxPages,
|
user={rest}
|
||||||
})
|
updateUserList={updateUserList}
|
||||||
}
|
/>
|
||||||
isDisabled={
|
<DeleteUserModal
|
||||||
paginationProps.page >= paginationProps.maxPages
|
user={rest}
|
||||||
}
|
updateUserList={updateUserList}
|
||||||
ml={4}
|
/>
|
||||||
icon={<FaAngleDoubleRight />}
|
{user.revoked_timestamp ? (
|
||||||
/>
|
<MenuItem
|
||||||
</Tooltip>
|
onClick={() =>
|
||||||
</Flex>
|
updateAccessHandler(
|
||||||
|
user.id,
|
||||||
|
updateAccessActions.ENABLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Enable Access
|
||||||
|
</MenuItem>
|
||||||
|
) : (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() =>
|
||||||
|
updateAccessHandler(
|
||||||
|
user.id,
|
||||||
|
updateAccessActions.REVOKE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Revoke Access
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{user.is_multi_factor_auth_enabled ? (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => multiFactorAuthUpdateHandler(user)}
|
||||||
|
>
|
||||||
|
Disable MFA
|
||||||
|
</MenuItem>
|
||||||
|
) : (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => multiFactorAuthUpdateHandler(user)}
|
||||||
|
>
|
||||||
|
Enable MFA
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tbody>
|
||||||
|
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
|
||||||
|
<TableCaption>
|
||||||
|
<Flex
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
m="2% 0"
|
||||||
|
>
|
||||||
|
<Flex flex="1">
|
||||||
|
<Tooltip label="First Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={paginationProps.page <= 1}
|
||||||
|
mr={4}
|
||||||
|
icon={<FaAngleDoubleLeft />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Previous Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: paginationProps.page - 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={paginationProps.page <= 1}
|
||||||
|
icon={<FaAngleLeft />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
</TableCaption>
|
<Flex
|
||||||
)}
|
flex="8"
|
||||||
</Table>
|
justifyContent="space-evenly"
|
||||||
</TableContainer>
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Text mr={8}>
|
||||||
|
Page{' '}
|
||||||
|
<Text fontWeight="bold" as="span">
|
||||||
|
{paginationProps.page}
|
||||||
|
</Text>{' '}
|
||||||
|
of{' '}
|
||||||
|
<Text fontWeight="bold" as="span">
|
||||||
|
{paginationProps.maxPages}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Text flexShrink="0">Go to page:</Text>{' '}
|
||||||
|
<NumberInput
|
||||||
|
ml={2}
|
||||||
|
mr={8}
|
||||||
|
w={28}
|
||||||
|
min={1}
|
||||||
|
max={paginationProps.maxPages}
|
||||||
|
onChange={(value) =>
|
||||||
|
paginationHandler({
|
||||||
|
page: parseInt(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
value={paginationProps.page}
|
||||||
|
>
|
||||||
|
<NumberInputField />
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
|
</Flex>
|
||||||
|
<Select
|
||||||
|
w={32}
|
||||||
|
value={paginationProps.limit}
|
||||||
|
onChange={(e) =>
|
||||||
|
paginationHandler({
|
||||||
|
page: 1,
|
||||||
|
limit: parseInt(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{getLimits(paginationProps).map((pageSize) => (
|
||||||
|
<option key={pageSize} value={pageSize}>
|
||||||
|
Show {pageSize}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Flex>
|
||||||
|
<Flex flex="1">
|
||||||
|
<Tooltip label="Next Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: paginationProps.page + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={
|
||||||
|
paginationProps.page >= paginationProps.maxPages
|
||||||
|
}
|
||||||
|
icon={<FaAngleRight />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Last Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: paginationProps.maxPages,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={
|
||||||
|
paginationProps.page >= paginationProps.maxPages
|
||||||
|
}
|
||||||
|
ml={4}
|
||||||
|
icon={<FaAngleDoubleRight />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</TableCaption>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
) : (
|
) : (
|
||||||
<Flex
|
<Flex
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
IconButton,
|
IconButton,
|
||||||
Menu,
|
Menu,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
|
MenuItem,
|
||||||
MenuList,
|
MenuList,
|
||||||
NumberDecrementStepper,
|
NumberDecrementStepper,
|
||||||
NumberIncrementStepper,
|
NumberIncrementStepper,
|
||||||
|
@ -39,7 +40,7 @@ import UpdateWebhookModal from '../components/UpdateWebhookModal';
|
||||||
import {
|
import {
|
||||||
pageLimits,
|
pageLimits,
|
||||||
WebhookInputDataFields,
|
WebhookInputDataFields,
|
||||||
UpdateModalViews,
|
UpdateWebhookModalViews,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { WebhooksDataQuery } from '../graphql/queries';
|
import { WebhooksDataQuery } from '../graphql/queries';
|
||||||
import DeleteWebhookModal from '../components/DeleteWebhookModal';
|
import DeleteWebhookModal from '../components/DeleteWebhookModal';
|
||||||
|
@ -56,7 +57,6 @@ interface paginationPropTypes {
|
||||||
interface webhookDataTypes {
|
interface webhookDataTypes {
|
||||||
[WebhookInputDataFields.ID]: string;
|
[WebhookInputDataFields.ID]: string;
|
||||||
[WebhookInputDataFields.EVENT_NAME]: string;
|
[WebhookInputDataFields.EVENT_NAME]: string;
|
||||||
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
|
|
||||||
[WebhookInputDataFields.ENDPOINT]: string;
|
[WebhookInputDataFields.ENDPOINT]: string;
|
||||||
[WebhookInputDataFields.ENABLED]: boolean;
|
[WebhookInputDataFields.ENABLED]: boolean;
|
||||||
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
|
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
|
||||||
|
@ -125,7 +125,7 @@ const Webhooks = () => {
|
||||||
Webhooks
|
Webhooks
|
||||||
</Text>
|
</Text>
|
||||||
<UpdateWebhookModal
|
<UpdateWebhookModal
|
||||||
view={UpdateModalViews.ADD}
|
view={UpdateWebhookModalViews.ADD}
|
||||||
fetchWebookData={fetchWebookData}
|
fetchWebookData={fetchWebookData}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -135,7 +135,6 @@ const Webhooks = () => {
|
||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Event Name</Th>
|
<Th>Event Name</Th>
|
||||||
<Th>Event Description</Th>
|
|
||||||
<Th>Endpoint</Th>
|
<Th>Endpoint</Th>
|
||||||
<Th>Enabled</Th>
|
<Th>Enabled</Th>
|
||||||
<Th>Headers</Th>
|
<Th>Headers</Th>
|
||||||
|
@ -149,10 +148,7 @@ const Webhooks = () => {
|
||||||
style={{ fontSize: 14 }}
|
style={{ fontSize: 14 }}
|
||||||
>
|
>
|
||||||
<Td maxW="300">
|
<Td maxW="300">
|
||||||
{webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]}
|
{webhook[WebhookInputDataFields.EVENT_NAME]}
|
||||||
</Td>
|
|
||||||
<Td maxW="300">
|
|
||||||
{webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
|
|
||||||
</Td>
|
</Td>
|
||||||
<Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td>
|
<Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
|
@ -175,12 +171,12 @@ const Webhooks = () => {
|
||||||
label={JSON.stringify(
|
label={JSON.stringify(
|
||||||
webhook[WebhookInputDataFields.HEADERS],
|
webhook[WebhookInputDataFields.HEADERS],
|
||||||
null,
|
null,
|
||||||
' ',
|
' '
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Tag size="sm" variant="outline" colorScheme="gray">
|
<Tag size="sm" variant="outline" colorScheme="gray">
|
||||||
{Object.keys(
|
{Object.keys(
|
||||||
webhook[WebhookInputDataFields.HEADERS] || {},
|
webhook[WebhookInputDataFields.HEADERS] || {}
|
||||||
)?.length.toString()}
|
)?.length.toString()}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -200,7 +196,7 @@ const Webhooks = () => {
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<UpdateWebhookModal
|
<UpdateWebhookModal
|
||||||
view={UpdateModalViews.Edit}
|
view={UpdateWebhookModalViews.Edit}
|
||||||
selectedWebhook={webhook}
|
selectedWebhook={webhook}
|
||||||
fetchWebookData={fetchWebookData}
|
fetchWebookData={fetchWebookData}
|
||||||
/>
|
/>
|
||||||
|
@ -269,7 +265,7 @@ const Webhooks = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<Text>Go to page:</Text>{' '}
|
<Text flexShrink="0">Go to page:</Text>{' '}
|
||||||
<NumberInput
|
<NumberInput
|
||||||
ml={2}
|
ml={2}
|
||||||
mr={8}
|
mr={8}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Outlet, Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAuthContext } from '../contexts/AuthContext';
|
import { useAuthContext } from '../contexts/AuthContext';
|
||||||
import { DashboardLayout } from '../layouts/DashboardLayout';
|
import { DashboardLayout } from '../layouts/DashboardLayout';
|
||||||
import EmailTemplates from '../pages/EmailTemplates';
|
|
||||||
|
|
||||||
const Auth = lazy(() => import('../pages/Auth'));
|
const Auth = lazy(() => import('../pages/Auth'));
|
||||||
const Environment = lazy(() => import('../pages/Environment'));
|
const Environment = lazy(() => import('../pages/Environment'));
|
||||||
|
@ -32,7 +31,6 @@ export const AppRoutes = () => {
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="users" element={<Users />} />
|
<Route path="users" element={<Users />} />
|
||||||
<Route path="webhooks" element={<Webhooks />} />
|
<Route path="webhooks" element={<Webhooks />} />
|
||||||
<Route path="email-templates" element={<EmailTemplates />} />
|
|
||||||
<Route path="*" element={<Home />} />
|
<Route path="*" element={<Home />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
@ -29,16 +29,19 @@ const fallbackCopyTextToClipboard = (text: string) => {
|
||||||
document.body.removeChild(textArea);
|
document.body.removeChild(textArea);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const copyTextToClipboard = async (text: string) => {
|
export const copyTextToClipboard = (text: string) => {
|
||||||
if (!navigator.clipboard) {
|
if (!navigator.clipboard) {
|
||||||
fallbackCopyTextToClipboard(text);
|
fallbackCopyTextToClipboard(text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
navigator.clipboard.writeText(text).then(
|
||||||
navigator.clipboard.writeText(text);
|
() => {
|
||||||
} catch (err) {
|
console.log('Async: Copying to clipboard was successful!');
|
||||||
throw err;
|
},
|
||||||
}
|
(err) => {
|
||||||
|
console.error('Async: Could not copy text: ', err);
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getObjectDiff = (obj1: any, obj2: any) => {
|
export const getObjectDiff = (obj1: any, obj2: any) => {
|
||||||
|
@ -67,7 +70,7 @@ export const validateEmail = (email: string) => {
|
||||||
return email
|
return email
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.match(
|
.match(
|
||||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||||
)
|
)
|
||||||
? true
|
? true
|
||||||
: false;
|
: false;
|
||||||
|
@ -78,7 +81,7 @@ export const validateURI = (uri: string) => {
|
||||||
return uri
|
return uri
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.match(
|
.match(
|
||||||
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/,
|
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/
|
||||||
)
|
)
|
||||||
? true
|
? true
|
||||||
: false;
|
: false;
|
||||||
|
|
|
@ -27,7 +27,7 @@ const parseCSV = (file: File, delimiter: string): Promise<dataTypes[]> => {
|
||||||
value: email.trim(),
|
value: email.trim(),
|
||||||
isInvalid: !validateEmail(email.trim()),
|
isInvalid: !validateEmail(email.trim()),
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
1887
dashboard/yarn.lock
1887
dashboard/yarn.lock
File diff suppressed because it is too large
Load Diff
20
go.mod
20
go.mod
|
@ -1,20 +0,0 @@
|
||||||
module server
|
|
||||||
|
|
||||||
go 1.21.5
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/99designs/gqlgen v0.17.43 // indirect
|
|
||||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/sosodev/duration v1.1.0 // indirect
|
|
||||||
github.com/urfave/cli/v2 v2.25.5 // indirect
|
|
||||||
github.com/vektah/gqlparser/v2 v2.5.11 // indirect
|
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
|
||||||
golang.org/x/mod v0.10.0 // indirect
|
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
|
||||||
golang.org/x/text v0.13.0 // indirect
|
|
||||||
golang.org/x/tools v0.9.3 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
31
go.sum
31
go.sum
|
@ -1,31 +0,0 @@
|
||||||
github.com/99designs/gqlgen v0.17.43 h1:I4SYg6ahjowErAQcHFVKy5EcWuwJ3+Xw9z2fLpuFCPo=
|
|
||||||
github.com/99designs/gqlgen v0.17.43/go.mod h1:lO0Zjy8MkZgBdv4T1U91x09r0e0WFOdhVUutlQs1Rsc=
|
|
||||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
|
||||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4=
|
|
||||||
github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
|
||||||
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
|
|
||||||
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
|
||||||
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
|
|
||||||
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
|
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
|
||||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
|
||||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
|
|
||||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
|
@ -1,39 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -x
|
|
||||||
set -m
|
|
||||||
|
|
||||||
sleep 15
|
|
||||||
|
|
||||||
# Setup index and memory quota
|
|
||||||
# curl -v -X POST http://127.0.0.1:8091/pools/default -d memoryQuota=300 -d indexMemoryQuota=300
|
|
||||||
|
|
||||||
# Setup services
|
|
||||||
curl -v http://127.0.0.1:8091/node/controller/setupServices -d services=kv%2Cn1ql%2Cindex
|
|
||||||
|
|
||||||
# Setup credentials
|
|
||||||
curl -v http://127.0.0.1:8091/settings/web -d port=8091 -d username=Administrator -d password=password
|
|
||||||
|
|
||||||
# Setup Memory Optimized Indexes
|
|
||||||
curl -i -u Administrator:password -X POST http://127.0.0.1:8091/settings/indexes -d 'storageMode=memory_optimized'
|
|
||||||
|
|
||||||
# Load travel-sample bucket
|
|
||||||
#curl -v -u Administrator:password -X POST http://127.0.0.1:8091/sampleBuckets/install -d '["travel-sample"]'
|
|
||||||
|
|
||||||
echo "Type: $TYPE"
|
|
||||||
|
|
||||||
if [ "$TYPE" = "WORKER" ]; then
|
|
||||||
echo "Sleeping ..."
|
|
||||||
sleep 15
|
|
||||||
|
|
||||||
#IP=`hostname -s`
|
|
||||||
IP=`hostname -I | cut -d ' ' -f1`
|
|
||||||
echo "IP: " $IP
|
|
||||||
|
|
||||||
echo "Auto Rebalance: $AUTO_REBALANCE"
|
|
||||||
if [ "$AUTO_REBALANCE" = "true" ]; then
|
|
||||||
couchbase-cli rebalance --cluster=$COUCHBASE_MASTER:8091 --user=Administrator --password=password --server-add=$IP --server-add-username=Administrator --server-add-password=password
|
|
||||||
else
|
|
||||||
couchbase-cli server-add --cluster=$COUCHBASE_MASTER:8091 --user=Administrator --password=password --server-add=$IP --server-add-username=Administrator --server-add-password=password
|
|
||||||
fi;
|
|
||||||
fi;
|
|
|
@ -1,25 +0,0 @@
|
||||||
package providers
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
// AuthenticatorConfig defines authenticator config
|
|
||||||
type AuthenticatorConfig struct {
|
|
||||||
// ScannerImage is the base64 of QR code image
|
|
||||||
ScannerImage string
|
|
||||||
// Secrets is the secret key
|
|
||||||
Secret string
|
|
||||||
// RecoveryCode is the list of recovery codes
|
|
||||||
RecoveryCodes []string
|
|
||||||
// RecoveryCodeMap is the map of recovery codes
|
|
||||||
RecoveryCodeMap map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provider defines authenticators provider
|
|
||||||
type Provider interface {
|
|
||||||
// Generate totp: to generate totp, store secret into db and returns base64 of QR code image
|
|
||||||
Generate(ctx context.Context, id string) (*AuthenticatorConfig, error)
|
|
||||||
// Validate totp: user passcode with secret stored in our db
|
|
||||||
Validate(ctx context.Context, passcode string, userID string) (bool, error)
|
|
||||||
// ValidateRecoveryCode totp: allows user to validate using recovery code incase if they lost their device
|
|
||||||
ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package totp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type provider struct {
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// TOTPConfig defines totp config
|
|
||||||
type TOTPConfig struct {
|
|
||||||
ScannerImage string
|
|
||||||
Secret string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewProvider returns a new totp provider
|
|
||||||
func NewProvider() (*provider, error) {
|
|
||||||
ctx := context.Background()
|
|
||||||
return &provider{
|
|
||||||
ctx: ctx,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
package totp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"image/png"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pquerna/otp/totp"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/authenticators/providers"
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/refs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generate generates a Time-Based One-Time Password (TOTP) for a user and returns the base64-encoded QR code for frontend display.
|
|
||||||
func (p *provider) Generate(ctx context.Context, id string) (*providers.AuthenticatorConfig, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
//get user details
|
|
||||||
user, err := db.Provider.GetUserByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// generate totp, Authenticators hash is valid for 30 seconds
|
|
||||||
key, err := totp.Generate(totp.GenerateOpts{
|
|
||||||
Issuer: "authorizer",
|
|
||||||
AccountName: refs.StringValue(user.Email),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
//generating image for key and encoding to base64 for displaying in frontend
|
|
||||||
img, err := key.Image(200, 200)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
png.Encode(&buf, img)
|
|
||||||
encodedText := crypto.EncryptB64(buf.String())
|
|
||||||
secret := key.Secret()
|
|
||||||
recoveryCodes := []string{}
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
recoveryCodes = append(recoveryCodes, uuid.NewString())
|
|
||||||
}
|
|
||||||
// Converting recoveryCodes to string
|
|
||||||
recoverCodesMap := map[string]bool{}
|
|
||||||
for i := 0; i < len(recoveryCodes); i++ {
|
|
||||||
recoverCodesMap[recoveryCodes[i]] = false
|
|
||||||
}
|
|
||||||
// Converting recoveryCodesMap to string
|
|
||||||
jsonData, err := json.Marshal(recoverCodesMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
recoveryCodesString := string(jsonData)
|
|
||||||
totpModel := &models.Authenticator{
|
|
||||||
Secret: secret,
|
|
||||||
RecoveryCodes: refs.NewStringRef(recoveryCodesString),
|
|
||||||
UserID: user.ID,
|
|
||||||
Method: constants.EnvKeyTOTPAuthenticator,
|
|
||||||
}
|
|
||||||
authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Failed to get authenticator details by user id, creating new record: ", err)
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
if authenticator == nil {
|
|
||||||
// if authenticator is nil then create new authenticator
|
|
||||||
_, err = db.Provider.AddAuthenticator(ctx, totpModel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
authenticator.Secret = secret
|
|
||||||
authenticator.RecoveryCodes = refs.NewStringRef(recoveryCodesString)
|
|
||||||
// if authenticator is not nil then update authenticator
|
|
||||||
_, err = db.Provider.UpdateAuthenticator(ctx, authenticator)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &providers.AuthenticatorConfig{
|
|
||||||
ScannerImage: encodedText,
|
|
||||||
Secret: secret,
|
|
||||||
RecoveryCodes: recoveryCodes,
|
|
||||||
RecoveryCodeMap: recoverCodesMap,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates a Time-Based One-Time Password (TOTP) against the stored TOTP secret for a user.
|
|
||||||
func (p *provider) Validate(ctx context.Context, passcode string, userID string) (bool, error) {
|
|
||||||
// get totp details
|
|
||||||
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
// validate totp
|
|
||||||
status := totp.Validate(passcode, totpModel.Secret)
|
|
||||||
// checks if user not signed in for totp and totp code is correct then VerifiedAt will be stored in db
|
|
||||||
if totpModel.VerifiedAt == nil && status {
|
|
||||||
timeNow := time.Now().Unix()
|
|
||||||
totpModel.VerifiedAt = &timeNow
|
|
||||||
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateRecoveryCode validates a Time-Based One-Time Password (TOTP) recovery code against the stored TOTP recovery code for a user.
|
|
||||||
func (p *provider) ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error) {
|
|
||||||
// get totp details
|
|
||||||
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
// convert recoveryCodes to map
|
|
||||||
recoveryCodesMap := map[string]bool{}
|
|
||||||
err = json.Unmarshal([]byte(refs.StringValue(totpModel.RecoveryCodes)), &recoveryCodesMap)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
// check if recovery code is valid
|
|
||||||
if val, ok := recoveryCodesMap[recoveryCode]; !ok {
|
|
||||||
return false, fmt.Errorf("invalid recovery code")
|
|
||||||
} else if val {
|
|
||||||
return false, fmt.Errorf("recovery code already used")
|
|
||||||
}
|
|
||||||
// update recovery code map
|
|
||||||
recoveryCodesMap[recoveryCode] = true
|
|
||||||
// convert recoveryCodesMap to string
|
|
||||||
jsonData, err := json.Marshal(recoveryCodesMap)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
recoveryCodesString := string(jsonData)
|
|
||||||
totpModel.RecoveryCodes = refs.NewStringRef(recoveryCodesString)
|
|
||||||
// update recovery code map in db
|
|
||||||
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package authenticators
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/authorizerdev/authorizer/server/authenticators/providers"
|
|
||||||
"github.com/authorizerdev/authorizer/server/authenticators/providers/totp"
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Provider is the global authenticators provider.
|
|
||||||
var Provider providers.Provider
|
|
||||||
|
|
||||||
// InitTOTPStore initializes the TOTP authenticator store if it's not disabled in the environment variables.
|
|
||||||
// It sets the global Provider variable to a new TOTP provider.
|
|
||||||
func InitTOTPStore() error {
|
|
||||||
var err error
|
|
||||||
isTOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
|
|
||||||
|
|
||||||
if !isTOTPEnvServiceDisabled {
|
|
||||||
Provider, err = totp.NewProvider()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -3,12 +3,8 @@ package constants
|
||||||
const (
|
const (
|
||||||
// AuthRecipeMethodBasicAuth is the basic_auth auth method
|
// AuthRecipeMethodBasicAuth is the basic_auth auth method
|
||||||
AuthRecipeMethodBasicAuth = "basic_auth"
|
AuthRecipeMethodBasicAuth = "basic_auth"
|
||||||
// AuthRecipeMethodMobileBasicAuth is the mobile basic_auth method, where user can signup using mobile number and password
|
|
||||||
AuthRecipeMethodMobileBasicAuth = "mobile_basic_auth"
|
|
||||||
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
|
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
|
||||||
AuthRecipeMethodMagicLinkLogin = "magic_link_login"
|
AuthRecipeMethodMagicLinkLogin = "magic_link_login"
|
||||||
// AuthRecipeMethodMobileOTP is the mobile_otp auth method
|
|
||||||
AuthRecipeMethodMobileOTP = "mobile_otp"
|
|
||||||
// AuthRecipeMethodGoogle is the google auth method
|
// AuthRecipeMethodGoogle is the google auth method
|
||||||
AuthRecipeMethodGoogle = "google"
|
AuthRecipeMethodGoogle = "google"
|
||||||
// AuthRecipeMethodGithub is the github auth method
|
// AuthRecipeMethodGithub is the github auth method
|
||||||
|
@ -19,14 +15,4 @@ const (
|
||||||
AuthRecipeMethodLinkedIn = "linkedin"
|
AuthRecipeMethodLinkedIn = "linkedin"
|
||||||
// AuthRecipeMethodApple is the apple auth method
|
// AuthRecipeMethodApple is the apple auth method
|
||||||
AuthRecipeMethodApple = "apple"
|
AuthRecipeMethodApple = "apple"
|
||||||
// AuthRecipeMethodDiscord is the discord auth method
|
|
||||||
AuthRecipeMethodDiscord = "discord"
|
|
||||||
// AuthRecipeMethodTwitter is the twitter auth method
|
|
||||||
AuthRecipeMethodTwitter = "twitter"
|
|
||||||
// AuthRecipeMethodMicrosoft is the microsoft auth method
|
|
||||||
AuthRecipeMethodMicrosoft = "microsoft"
|
|
||||||
// AuthRecipeMethodTwitch is the twitch auth method
|
|
||||||
AuthRecipeMethodTwitch = "twitch"
|
|
||||||
// AuthRecipeMethodRoblox is the roblox auth method
|
|
||||||
AuthRecipeMethodRoblox = "roblox"
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package constants
|
|
||||||
|
|
||||||
// Authenticators Methods
|
|
||||||
const (
|
|
||||||
// EnvKeyTOTPAuthenticator key for env variable TOTP
|
|
||||||
EnvKeyTOTPAuthenticator = "totp"
|
|
||||||
)
|
|
|
@ -5,6 +5,4 @@ const (
|
||||||
AppCookieName = "cookie"
|
AppCookieName = "cookie"
|
||||||
// AdminCookieName is the name of the cookie that is used to store the admin token
|
// AdminCookieName is the name of the cookie that is used to store the admin token
|
||||||
AdminCookieName = "authorizer-admin"
|
AdminCookieName = "authorizer-admin"
|
||||||
// MfaCookieName is the name of the cookie that is used to store the mfa session
|
|
||||||
MfaCookieName = "mfa"
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,8 +5,6 @@ const (
|
||||||
DbTypePostgres = "postgres"
|
DbTypePostgres = "postgres"
|
||||||
// DbTypeSqlite is the sqlite database type
|
// DbTypeSqlite is the sqlite database type
|
||||||
DbTypeSqlite = "sqlite"
|
DbTypeSqlite = "sqlite"
|
||||||
// DbTypeLibSQL is the libsql / Turso database type
|
|
||||||
DbTypeLibSQL = "libsql"
|
|
||||||
// DbTypeMysql is the mysql database type
|
// DbTypeMysql is the mysql database type
|
||||||
DbTypeMysql = "mysql"
|
DbTypeMysql = "mysql"
|
||||||
// DbTypeSqlserver is the sqlserver database type
|
// DbTypeSqlserver is the sqlserver database type
|
||||||
|
@ -27,8 +25,4 @@ const (
|
||||||
DbTypeCockroachDB = "cockroachdb"
|
DbTypeCockroachDB = "cockroachdb"
|
||||||
// DbTypePlanetScaleDB is the planetscale database type
|
// DbTypePlanetScaleDB is the planetscale database type
|
||||||
DbTypePlanetScaleDB = "planetscale"
|
DbTypePlanetScaleDB = "planetscale"
|
||||||
// DbTypeDynamoDB is the Dynamo database type
|
|
||||||
DbTypeDynamoDB = "dynamodb"
|
|
||||||
// DbTypeCouchbaseDB is the Couchbase database type
|
|
||||||
DbTypeCouchbaseDB = "couchbase"
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,12 +21,6 @@ const (
|
||||||
EnvKeyDatabaseType = "DATABASE_TYPE"
|
EnvKeyDatabaseType = "DATABASE_TYPE"
|
||||||
// EnvKeyDatabaseURL key for env variable DATABASE_URL
|
// EnvKeyDatabaseURL key for env variable DATABASE_URL
|
||||||
EnvKeyDatabaseURL = "DATABASE_URL"
|
EnvKeyDatabaseURL = "DATABASE_URL"
|
||||||
// EnvAwsRegion key for env variable AWS REGION
|
|
||||||
EnvAwsRegion = "AWS_REGION"
|
|
||||||
// EnvAwsAccessKeyID key for env variable AWS_ACCESS_KEY_ID
|
|
||||||
EnvAwsAccessKeyID = "AWS_ACCESS_KEY_ID"
|
|
||||||
// EnvAwsAccessKey key for env variable AWS_SECRET_ACCESS_KEY
|
|
||||||
EnvAwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
|
|
||||||
// EnvKeyDatabaseName key for env variable DATABASE_NAME
|
// EnvKeyDatabaseName key for env variable DATABASE_NAME
|
||||||
EnvKeyDatabaseName = "DATABASE_NAME"
|
EnvKeyDatabaseName = "DATABASE_NAME"
|
||||||
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
|
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
|
||||||
|
@ -43,13 +37,6 @@ const (
|
||||||
EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY"
|
EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY"
|
||||||
// EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT
|
// EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT
|
||||||
EnvKeyDatabaseCACert = "DATABASE_CA_CERT"
|
EnvKeyDatabaseCACert = "DATABASE_CA_CERT"
|
||||||
// EnvCouchbaseBucket key for env variable COUCHBASE_BUCKET
|
|
||||||
EnvCouchbaseBucket = "COUCHBASE_BUCKET"
|
|
||||||
// EnvCouchbaseBucketRAMQuotaMB key for env variable COUCHBASE_BUCKET_RAM_QUOTA
|
|
||||||
// This value should be parsed as number
|
|
||||||
EnvCouchbaseBucketRAMQuotaMB = "COUCHBASE_BUCKET_RAM_QUOTA"
|
|
||||||
// EnvCouchbaseBucket key for env variable COUCHBASE_SCOPE
|
|
||||||
EnvCouchbaseScope = "COUCHBASE_SCOPE"
|
|
||||||
// EnvKeySmtpHost key for env variable SMTP_HOST
|
// EnvKeySmtpHost key for env variable SMTP_HOST
|
||||||
EnvKeySmtpHost = "SMTP_HOST"
|
EnvKeySmtpHost = "SMTP_HOST"
|
||||||
// EnvKeySmtpPort key for env variable SMTP_PORT
|
// EnvKeySmtpPort key for env variable SMTP_PORT
|
||||||
|
@ -58,20 +45,10 @@ const (
|
||||||
EnvKeySmtpUsername = "SMTP_USERNAME"
|
EnvKeySmtpUsername = "SMTP_USERNAME"
|
||||||
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD
|
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD
|
||||||
EnvKeySmtpPassword = "SMTP_PASSWORD"
|
EnvKeySmtpPassword = "SMTP_PASSWORD"
|
||||||
// EnvKeySmtpLocalName key for env variable SMTP_LOCAL_NAME
|
|
||||||
EnvKeySmtpLocalName = "SMTP_LOCAL_NAME"
|
|
||||||
// EnvKeySenderEmail key for env variable SENDER_EMAIL
|
// EnvKeySenderEmail key for env variable SENDER_EMAIL
|
||||||
EnvKeySenderEmail = "SENDER_EMAIL"
|
EnvKeySenderEmail = "SENDER_EMAIL"
|
||||||
// EnvKeySenderName key for env variable SENDER_NAME
|
|
||||||
EnvKeySenderName = "SENDER_NAME"
|
|
||||||
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
||||||
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
|
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
|
||||||
// EnvKeyIsSMSServiceEnabled key for env variable IS_SMS_SERVICE_ENABLED
|
|
||||||
EnvKeyIsSMSServiceEnabled = "IS_SMS_SERVICE_ENABLED"
|
|
||||||
// EnvKeyAppCookieSecure key for env variable APP_COOKIE_SECURE
|
|
||||||
EnvKeyAppCookieSecure = "APP_COOKIE_SECURE"
|
|
||||||
// EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE
|
|
||||||
EnvKeyAdminCookieSecure = "ADMIN_COOKIE_SECURE"
|
|
||||||
// EnvKeyJwtType key for env variable JWT_TYPE
|
// EnvKeyJwtType key for env variable JWT_TYPE
|
||||||
EnvKeyJwtType = "JWT_TYPE"
|
EnvKeyJwtType = "JWT_TYPE"
|
||||||
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
||||||
|
@ -108,28 +85,6 @@ const (
|
||||||
EnvKeyAppleClientID = "APPLE_CLIENT_ID"
|
EnvKeyAppleClientID = "APPLE_CLIENT_ID"
|
||||||
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
|
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
|
||||||
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
|
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
|
||||||
// EnvKeyDiscordClientID key for env variable DISCORD_CLIENT_ID
|
|
||||||
EnvKeyDiscordClientID = "DISCORD_CLIENT_ID"
|
|
||||||
// EnvKeyDiscordClientSecret key for env variable DISCORD_CLIENT_SECRET
|
|
||||||
EnvKeyDiscordClientSecret = "DISCORD_CLIENT_SECRET"
|
|
||||||
// EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID
|
|
||||||
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
|
|
||||||
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
|
|
||||||
EnvKeyTwitterClientSecret = "TWITTER_CLIENT_SECRET"
|
|
||||||
// EnvKeyMicrosoftClientID key for env variable MICROSOFT_CLIENT_ID
|
|
||||||
EnvKeyMicrosoftClientID = "MICROSOFT_CLIENT_ID"
|
|
||||||
// EnvKeyMicrosoftActiveDirectoryTenantID key for env variable MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
|
|
||||||
EnvKeyMicrosoftActiveDirectoryTenantID = "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"
|
|
||||||
// EnvKeyMicrosoftClientSecret key for env variable MICROSOFT_CLIENT_SECRET
|
|
||||||
EnvKeyMicrosoftClientSecret = "MICROSOFT_CLIENT_SECRET"
|
|
||||||
// EnvKeyTwitchClientID key for env variable TWITCH_CLIENT_ID
|
|
||||||
EnvKeyTwitchClientID = "TWITCH_CLIENT_ID"
|
|
||||||
// EnvKeyTwitchClientSecret key for env variable TWITCH_CLIENT_SECRET
|
|
||||||
EnvKeyTwitchClientSecret = "TWITCH_CLIENT_SECRET"
|
|
||||||
// EnvKeyRobloxClientID key for env variable ROBLOX_CLIENT_ID
|
|
||||||
EnvKeyRobloxClientID = "ROBLOX_CLIENT_ID"
|
|
||||||
// EnvKeyRobloxClientSecret key for env variable ROBLOX_CLIENT_SECRET
|
|
||||||
EnvKeyRobloxClientSecret = "ROBLOX_CLIENT_SECRET"
|
|
||||||
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
|
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
|
||||||
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
||||||
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
||||||
|
@ -154,8 +109,6 @@ const (
|
||||||
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
|
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
|
||||||
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
|
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
|
||||||
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
|
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
|
||||||
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_MOBILE_BASIC_AUTH
|
|
||||||
EnvKeyDisableMobileBasicAuthentication = "DISABLE_MOBILE_BASIC_AUTHENTICATION"
|
|
||||||
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
|
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
|
||||||
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
|
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
|
||||||
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
|
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE
|
||||||
|
@ -172,18 +125,6 @@ const (
|
||||||
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
|
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
|
||||||
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
|
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
|
||||||
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
|
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
|
||||||
// EnvKeyDisableTOTPLogin is key for env variable DISABLE_TOTP_LOGIN
|
|
||||||
// this variable is used to completely disable totp verification
|
|
||||||
EnvKeyDisableTOTPLogin = "DISABLE_TOTP_LOGIN"
|
|
||||||
// EnvKeyDisableMailOTPLogin is key for env variable DISABLE_MAIL_OTP_LOGIN
|
|
||||||
// this variable is used to completely disable totp verification
|
|
||||||
EnvKeyDisableMailOTPLogin = "DISABLE_MAIL_OTP_LOGIN"
|
|
||||||
// EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION
|
|
||||||
// this variable is used to disable phone verification
|
|
||||||
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"
|
|
||||||
// EnvKeyDisablePlayGround is key for env variable DISABLE_PLAYGROUND
|
|
||||||
// this variable will disable or enable playground use in dashboard
|
|
||||||
EnvKeyDisablePlayGround = "DISABLE_PLAYGROUND"
|
|
||||||
|
|
||||||
// Slice variables
|
// Slice variables
|
||||||
// EnvKeyRoles key for env variable ROLES
|
// EnvKeyRoles key for env variable ROLES
|
||||||
|
@ -194,22 +135,4 @@ const (
|
||||||
EnvKeyDefaultRoles = "DEFAULT_ROLES"
|
EnvKeyDefaultRoles = "DEFAULT_ROLES"
|
||||||
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
|
// EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS
|
||||||
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
|
EnvKeyAllowedOrigins = "ALLOWED_ORIGINS"
|
||||||
|
|
||||||
// For oauth/openid/authorize
|
|
||||||
// EnvKeyDefaultAuthorizeResponseType key for env variable DEFAULT_AUTHORIZE_RESPONSE_TYPE
|
|
||||||
// This env is used for setting default response type in authorize handler
|
|
||||||
EnvKeyDefaultAuthorizeResponseType = "DEFAULT_AUTHORIZE_RESPONSE_TYPE"
|
|
||||||
// EnvKeyDefaultAuthorizeResponseMode key for env variable DEFAULT_AUTHORIZE_RESPONSE_MODE
|
|
||||||
// This env is used for setting default response mode in authorize handler
|
|
||||||
EnvKeyDefaultAuthorizeResponseMode = "DEFAULT_AUTHORIZE_RESPONSE_MODE"
|
|
||||||
|
|
||||||
// Twilio env variables
|
|
||||||
// EnvKeyTwilioAPIKey key for env variable TWILIO_API_KEY
|
|
||||||
EnvKeyTwilioAPIKey = "TWILIO_API_KEY"
|
|
||||||
// EnvKeyTwilioAPISecret key for env variable TWILIO_API_SECRET
|
|
||||||
EnvKeyTwilioAPISecret = "TWILIO_API_SECRET"
|
|
||||||
// EnvKeyTwilioAccountSID key for env variable TWILIO_ACCOUNT_SID
|
|
||||||
EnvKeyTwilioAccountSID = "TWILIO_ACCOUNT_SID"
|
|
||||||
// EnvKeyTwilioSender key for env variable TWILIO_SENDER
|
|
||||||
EnvKeyTwilioSender = "TWILIO_SENDER"
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
package constants
|
|
||||||
|
|
||||||
const (
|
|
||||||
// - query: for Authorization Code grant. 302 Found triggers redirect.
|
|
||||||
ResponseModeQuery = "query"
|
|
||||||
// - fragment: for Implicit grant. 302 Found triggers redirect.
|
|
||||||
ResponseModeFragment = "fragment"
|
|
||||||
// - form_post: 200 OK with response parameters embedded in an HTML form as hidden parameters.
|
|
||||||
ResponseModeFormPost = "form_post"
|
|
||||||
// - web_message: For Silent Authentication. Uses HTML5 web messaging.
|
|
||||||
ResponseModeWebMessage = "web_message"
|
|
||||||
|
|
||||||
// For the Authorization Code grant, use response_type=code to include the authorization code.
|
|
||||||
ResponseTypeCode = "code"
|
|
||||||
// For the Implicit grant, use response_type=token to include an access token.
|
|
||||||
ResponseTypeToken = "token"
|
|
||||||
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
|
||||||
ResponseTypeIDToken = "id_token"
|
|
||||||
|
|
||||||
// Constant indicating the "signup" screen hint for customizing authentication process and redirect to a signup page.
|
|
||||||
ScreenHintSignUp = "signup"
|
|
||||||
)
|
|
|
@ -9,19 +9,9 @@ const (
|
||||||
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
|
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
|
||||||
GithubUserInfoURL = "https://api.github.com/user"
|
GithubUserInfoURL = "https://api.github.com/user"
|
||||||
// Get github user emails when user info email is empty Ref: https://stackoverflow.com/a/35387123
|
// Get github user emails when user info email is empty Ref: https://stackoverflow.com/a/35387123
|
||||||
GithubUserEmails = "https://api.github.com/user/emails"
|
GithubUserEmails = "https://api/github.com/user/emails"
|
||||||
|
|
||||||
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
|
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
|
||||||
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
|
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
|
||||||
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
|
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
|
||||||
|
|
||||||
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
|
|
||||||
|
|
||||||
// RobloxUserInfoURL is the URL to get user info from Roblox
|
|
||||||
RobloxUserInfoURL = "https://apis.roblox.com/oauth/v1/userinfo"
|
|
||||||
|
|
||||||
DiscordUserInfoURL = "https://discord.com/api/oauth2/@me"
|
|
||||||
// Get microsoft user info.
|
|
||||||
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
|
|
||||||
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,19 +9,4 @@ const (
|
||||||
VerificationTypeUpdateEmail = "update_email"
|
VerificationTypeUpdateEmail = "update_email"
|
||||||
// VerificationTypeForgotPassword is the forgot_password verification type
|
// VerificationTypeForgotPassword is the forgot_password verification type
|
||||||
VerificationTypeForgotPassword = "forgot_password"
|
VerificationTypeForgotPassword = "forgot_password"
|
||||||
// VerificationTypeInviteMember is the invite_member verification type
|
|
||||||
VerificationTypeInviteMember = "invite_member"
|
|
||||||
// VerificationTypeOTP is the otp verification type
|
|
||||||
VerificationTypeOTP = "verify_otp"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// VerificationTypes is slice of all verification types
|
|
||||||
VerificationTypes = []string{
|
|
||||||
VerificationTypeBasicAuthSignup,
|
|
||||||
VerificationTypeMagicLinkLogin,
|
|
||||||
VerificationTypeUpdateEmail,
|
|
||||||
VerificationTypeForgotPassword,
|
|
||||||
VerificationTypeInviteMember,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,6 +15,4 @@ const (
|
||||||
UserAccessEnabledWebhookEvent = `user.access_enabled`
|
UserAccessEnabledWebhookEvent = `user.access_enabled`
|
||||||
// UserDeletedWebhookEvent name for user deleted event
|
// UserDeletedWebhookEvent name for user deleted event
|
||||||
UserDeletedWebhookEvent = `user.deleted`
|
UserDeletedWebhookEvent = `user.deleted`
|
||||||
// UserDeactivatedWebhookEvent name for user deactivated event
|
|
||||||
UserDeactivatedWebhookEvent = `user.deactivated`
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,24 +3,15 @@ package cookie
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetAdminCookie sets the admin cookie in the response
|
// SetAdminCookie sets the admin cookie in the response
|
||||||
func SetAdminCookie(gc *gin.Context, token string) {
|
func SetAdminCookie(gc *gin.Context, token string) {
|
||||||
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure)
|
secure := true
|
||||||
if err != nil {
|
httpOnly := true
|
||||||
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
|
|
||||||
adminCookieSecure = true
|
|
||||||
}
|
|
||||||
|
|
||||||
secure := adminCookieSecure
|
|
||||||
httpOnly := adminCookieSecure
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
|
||||||
|
@ -44,14 +35,8 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
|
||||||
|
|
||||||
// DeleteAdminCookie sets the response cookie to empty
|
// DeleteAdminCookie sets the response cookie to empty
|
||||||
func DeleteAdminCookie(gc *gin.Context) {
|
func DeleteAdminCookie(gc *gin.Context) {
|
||||||
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure)
|
secure := true
|
||||||
if err != nil {
|
httpOnly := true
|
||||||
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
|
|
||||||
adminCookieSecure = true
|
|
||||||
}
|
|
||||||
|
|
||||||
secure := adminCookieSecure
|
|
||||||
httpOnly := adminCookieSecure
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)
|
||||||
|
|
|
@ -4,24 +4,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetSession sets the session cookie in the response
|
// SetSession sets the session cookie in the response
|
||||||
func SetSession(gc *gin.Context, sessionID string) {
|
func SetSession(gc *gin.Context, sessionID string) {
|
||||||
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
secure := true
|
||||||
if err != nil {
|
httpOnly := true
|
||||||
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
|
||||||
appCookieSecure = true
|
|
||||||
}
|
|
||||||
|
|
||||||
secure := appCookieSecure
|
|
||||||
httpOnly := appCookieSecure
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
domain := parsers.GetDomainName(hostname)
|
domain := parsers.GetDomainName(hostname)
|
||||||
|
@ -29,34 +20,18 @@ func SetSession(gc *gin.Context, sessionID string) {
|
||||||
domain = "." + domain
|
domain = "." + domain
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
|
|
||||||
// Example person using custom UI on their app domain and making request to authorizer domain.
|
|
||||||
// For more information check:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
|
||||||
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
|
|
||||||
// TODO add ability to sameSite = none / strict from dashboard
|
|
||||||
if !appCookieSecure {
|
|
||||||
gc.SetSameSite(http.SameSiteLaxMode)
|
|
||||||
} else {
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
|
||||||
}
|
|
||||||
// TODO allow configuring from dashboard
|
// TODO allow configuring from dashboard
|
||||||
year := 60 * 60 * 24 * 365
|
year := 60 * 60 * 24 * 365
|
||||||
|
|
||||||
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
gc.SetCookie(constants.AppCookieName+"_session", sessionID, year, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.AppCookieName+"_session", sessionID, year, "/", host, secure, httpOnly)
|
||||||
gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
|
gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSession sets session cookies to expire
|
// DeleteSession sets session cookies to expire
|
||||||
func DeleteSession(gc *gin.Context) {
|
func DeleteSession(gc *gin.Context) {
|
||||||
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
secure := true
|
||||||
if err != nil {
|
httpOnly := true
|
||||||
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
|
||||||
appCookieSecure = true
|
|
||||||
}
|
|
||||||
|
|
||||||
secure := appCookieSecure
|
|
||||||
httpOnly := appCookieSecure
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
domain := parsers.GetDomainName(hostname)
|
domain := parsers.GetDomainName(hostname)
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
package cookie
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetMfaSession sets the mfa session cookie in the response
|
|
||||||
func SetMfaSession(gc *gin.Context, sessionID string) {
|
|
||||||
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
|
||||||
appCookieSecure = true
|
|
||||||
}
|
|
||||||
|
|
||||||
secure := appCookieSecure
|
|
||||||
httpOnly := appCookieSecure
|
|
||||||
hostname := parsers.GetHost(gc)
|
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
|
||||||
domain := parsers.GetDomainName(hostname)
|
|
||||||
if domain != "localhost" {
|
|
||||||
domain = "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
|
|
||||||
// Example person using custom UI on their app domain and making request to authorizer domain.
|
|
||||||
// For more information check:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
|
||||||
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
|
|
||||||
// TODO add ability to sameSite = none / strict from dashboard
|
|
||||||
if !appCookieSecure {
|
|
||||||
gc.SetSameSite(http.SameSiteLaxMode)
|
|
||||||
} else {
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
|
||||||
}
|
|
||||||
// TODO allow configuring from dashboard
|
|
||||||
age := 60
|
|
||||||
|
|
||||||
gc.SetCookie(constants.MfaCookieName+"_session", sessionID, age, "/", host, secure, httpOnly)
|
|
||||||
gc.SetCookie(constants.MfaCookieName+"_session_domain", sessionID, age, "/", domain, secure, httpOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteMfaSession deletes the mfa session cookies to expire
|
|
||||||
func DeleteMfaSession(gc *gin.Context) {
|
|
||||||
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
|
||||||
appCookieSecure = true
|
|
||||||
}
|
|
||||||
|
|
||||||
secure := appCookieSecure
|
|
||||||
httpOnly := appCookieSecure
|
|
||||||
hostname := parsers.GetHost(gc)
|
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
|
||||||
domain := parsers.GetDomainName(hostname)
|
|
||||||
if domain != "localhost" {
|
|
||||||
domain = "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
|
||||||
gc.SetCookie(constants.MfaCookieName+"_session", "", -1, "/", host, secure, httpOnly)
|
|
||||||
gc.SetCookie(constants.MfaCookieName+"_session_domain", "", -1, "/", domain, secure, httpOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMfaSession gets the mfa session cookie from context
|
|
||||||
func GetMfaSession(gc *gin.Context) (string, error) {
|
|
||||||
var cookie *http.Cookie
|
|
||||||
var err error
|
|
||||||
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session")
|
|
||||||
if err != nil {
|
|
||||||
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session_domain")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decodedValue, err := url.PathUnescape(cookie.Value)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return decodedValue, nil
|
|
||||||
}
|
|
|
@ -1,9 +1,7 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
@ -127,27 +125,12 @@ func EncryptEnvData(data map[string]interface{}) (string, error) {
|
||||||
return EncryptB64(string(encryptedConfig)), nil
|
return EncryptB64(string(encryptedConfig)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSHA256 calculates the SHA-256 hash of a string
|
|
||||||
func getSHA256(input string) string {
|
|
||||||
hash := sha256.New()
|
|
||||||
hash.Write([]byte(input))
|
|
||||||
return hex.EncodeToString(hash.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyPassword compares a stored hashed password with a user-provided password
|
|
||||||
func VerifyPassword(storedHashedPassword, userProvidedPassword string) error {
|
|
||||||
// CompareHashAndPassword returns nil on success
|
|
||||||
passwordSHA256 := getSHA256(userProvidedPassword)
|
|
||||||
err := bcrypt.CompareHashAndPassword([]byte(storedHashedPassword), []byte(passwordSHA256))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptPassword is used for encrypting password
|
// EncryptPassword is used for encrypting password
|
||||||
func EncryptPassword(password string) (string, error) {
|
func EncryptPassword(password string) (string, error) {
|
||||||
passwordSHA256 := getSHA256(password)
|
pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
pw, err := bcrypt.GenerateFromPassword([]byte(passwordSHA256), bcrypt.DefaultCost)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(pw), nil
|
return string(pw), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
@ -118,24 +116,3 @@ func AsRSAStr(privateKey *rsa.PrivateKey, publickKey *rsa.PublicKey) (string, st
|
||||||
|
|
||||||
return privParsedPem, pubParsedPem, nil
|
return privParsedPem, pubParsedPem, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncryptRSA(message string, key rsa.PublicKey) (string, error) {
|
|
||||||
label := []byte("OAEP Encrypted")
|
|
||||||
rng := rand.Reader
|
|
||||||
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(message), label)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecryptRSA(cipherText string, privateKey rsa.PrivateKey) (string, error) {
|
|
||||||
ct, _ := base64.StdEncoding.DecodeString(cipherText)
|
|
||||||
label := []byte("OAEP Encrypted")
|
|
||||||
rng := rand.Reader
|
|
||||||
plaintext, err := rsa.DecryptOAEP(sha256.New(), rng, &privateKey, ct, label)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(plaintext), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers"
|
"github.com/authorizerdev/authorizer/server/db/providers"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/cassandradb"
|
"github.com/authorizerdev/authorizer/server/db/providers/cassandradb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/couchbase"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/dynamodb"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
@ -22,12 +20,10 @@ func InitDB() error {
|
||||||
|
|
||||||
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
|
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
|
||||||
|
|
||||||
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB && envs.DatabaseType != constants.DbTypeDynamoDB && envs.DatabaseType != constants.DbTypeCouchbaseDB
|
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB
|
||||||
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
|
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
|
||||||
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
|
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
|
||||||
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
|
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
|
||||||
isDynamoDB := envs.DatabaseType == constants.DbTypeDynamoDB
|
|
||||||
isCouchbaseDB := envs.DatabaseType == constants.DbTypeCouchbaseDB
|
|
||||||
|
|
||||||
if isSQL {
|
if isSQL {
|
||||||
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
|
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
|
||||||
|
@ -37,6 +33,7 @@ func InitDB() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isArangoDB {
|
if isArangoDB {
|
||||||
log.Info("Initializing ArangoDB Driver")
|
log.Info("Initializing ArangoDB Driver")
|
||||||
Provider, err = arangodb.NewProvider()
|
Provider, err = arangodb.NewProvider()
|
||||||
|
@ -64,23 +61,5 @@ func InitDB() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDynamoDB {
|
|
||||||
log.Info("Initializing DynamoDB Driver for: ", envs.DatabaseType)
|
|
||||||
Provider, err = dynamodb.NewProvider()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to initialize DynamoDB driver: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isCouchbaseDB {
|
|
||||||
log.Info("Initializing CouchbaseDB Driver for: ", envs.DatabaseType)
|
|
||||||
Provider, err = couchbase.NewProvider()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to initialize Couchbase driver: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package models
|
|
||||||
|
|
||||||
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
|
||||||
|
|
||||||
// Authenticators model for db
|
|
||||||
type Authenticator struct {
|
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
|
||||||
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
|
|
||||||
Method string `json:"method" bson:"method" cql:"method" dynamo:"method"`
|
|
||||||
Secret string `json:"secret" bson:"secret" cql:"secret" dynamo:"secret"`
|
|
||||||
RecoveryCodes *string `json:"recovery_codes" bson:"recovery_codes" cql:"recovery_codes" dynamo:"recovery_codes"`
|
|
||||||
VerifiedAt *int64 `json:"verified_at" bson:"verified_at" cql:"verified_at" dynamo:"verified_at"`
|
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
|
||||||
}
|
|
|
@ -9,14 +9,12 @@ import (
|
||||||
|
|
||||||
// EmailTemplate model for database
|
// EmailTemplate model for database
|
||||||
type EmailTemplate struct {
|
type EmailTemplate struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
|
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||||
Subject string `json:"subject" bson:"subject" cql:"subject" dynamo:"subject"`
|
Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"`
|
||||||
Template string `json:"template" bson:"template" cql:"template" dynamo:"template"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
Design string `json:"design" bson:"design" cql:"design" dynamo:"design"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsAPIEmailTemplate to return email template as graphql response object
|
// AsAPIEmailTemplate to return email template as graphql response object
|
||||||
|
@ -28,9 +26,7 @@ func (e *EmailTemplate) AsAPIEmailTemplate() *model.EmailTemplate {
|
||||||
return &model.EmailTemplate{
|
return &model.EmailTemplate{
|
||||||
ID: id,
|
ID: id,
|
||||||
EventName: e.EventName,
|
EventName: e.EventName,
|
||||||
Subject: e.Subject,
|
|
||||||
Template: e.Template,
|
Template: e.Template,
|
||||||
Design: e.Design,
|
|
||||||
CreatedAt: refs.NewInt64Ref(e.CreatedAt),
|
CreatedAt: refs.NewInt64Ref(e.CreatedAt),
|
||||||
UpdatedAt: refs.NewInt64Ref(e.UpdatedAt),
|
UpdatedAt: refs.NewInt64Ref(e.UpdatedAt),
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,10 @@ package models
|
||||||
|
|
||||||
// Env model for db
|
// Env model for db
|
||||||
type Env struct {
|
type Env struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
EnvData string `json:"env" bson:"env" cql:"env" dynamo:"env"`
|
EnvData string `gorm:"type:text" json:"env" bson:"env" cql:"env"`
|
||||||
Hash string `json:"hash" bson:"hash" cql:"hash" dynamo:"hash"`
|
Hash string `gorm:"type:text" json:"hash" bson:"hash" cql:"hash"`
|
||||||
EncryptionKey string `json:"encryption_key" bson:"encryption_key" cql:"encryption_key" dynamo:"encryption_key"` // couchbase has "hash" as reserved keyword so we cannot use it. This will be empty for other dbs.
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
// CollectionList / Tables available for authorizer in the database
|
// Collections / Tables available for authorizer in the database
|
||||||
type CollectionList struct {
|
type CollectionList struct {
|
||||||
User string
|
User string
|
||||||
VerificationRequest string
|
VerificationRequest string
|
||||||
Session string
|
Session string
|
||||||
Env string
|
Env string
|
||||||
Webhook string
|
Webhook string
|
||||||
WebhookLog string
|
WebhookLog string
|
||||||
EmailTemplate string
|
EmailTemplate string
|
||||||
OTP string
|
OTP string
|
||||||
SMSVerificationRequest string
|
|
||||||
Authenticators string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -19,15 +17,13 @@ var (
|
||||||
Prefix = "authorizer_"
|
Prefix = "authorizer_"
|
||||||
// Collections / Tables available for authorizer in the database (used for dbs other than gorm)
|
// Collections / Tables available for authorizer in the database (used for dbs other than gorm)
|
||||||
Collections = CollectionList{
|
Collections = CollectionList{
|
||||||
User: Prefix + "users",
|
User: Prefix + "users",
|
||||||
VerificationRequest: Prefix + "verification_requests",
|
VerificationRequest: Prefix + "verification_requests",
|
||||||
Session: Prefix + "sessions",
|
Session: Prefix + "sessions",
|
||||||
Env: Prefix + "env",
|
Env: Prefix + "env",
|
||||||
Webhook: Prefix + "webhooks",
|
Webhook: Prefix + "webhooks",
|
||||||
WebhookLog: Prefix + "webhook_logs",
|
WebhookLog: Prefix + "webhook_logs",
|
||||||
EmailTemplate: Prefix + "email_templates",
|
EmailTemplate: Prefix + "email_templates",
|
||||||
OTP: Prefix + "otps",
|
OTP: Prefix + "otps",
|
||||||
SMSVerificationRequest: Prefix + "sms_verification_requests",
|
|
||||||
Authenticators: Prefix + "authenticators",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,24 +1,12 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
const (
|
|
||||||
// FieldName email is the field name for email
|
|
||||||
FieldNameEmail = "email"
|
|
||||||
// FieldNamePhoneNumber is the field name for phone number
|
|
||||||
FieldNamePhoneNumber = "phone_number"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OTP model for database
|
// OTP model for database
|
||||||
type OTP struct {
|
type OTP struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
Email string `gorm:"index" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||||
PhoneNumber string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
Otp string `json:"otp" bson:"otp" cql:"otp"`
|
||||||
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Paging struct {
|
|
||||||
ID string `json:"id,omitempty" dynamo:"id,hash"`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ package models
|
||||||
|
|
||||||
// Session model for db
|
// Session model for db
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
|
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id"`
|
||||||
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent" dynamo:"user_agent"`
|
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent"`
|
||||||
IP string `json:"ip" bson:"ip" cql:"ip" dynamo:"ip"`
|
IP string `json:"ip" bson:"ip" cql:"ip"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -12,35 +11,33 @@ import (
|
||||||
|
|
||||||
// User model for db
|
// User model for db
|
||||||
type User struct {
|
type User struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
|
|
||||||
Email *string `gorm:"index" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"`
|
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
|
||||||
Password *string `json:"password" bson:"password" cql:"password" dynamo:"password"`
|
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
|
||||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"`
|
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
|
||||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name" dynamo:"given_name"`
|
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
|
||||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name" dynamo:"family_name"`
|
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
|
||||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name" dynamo:"middle_name"`
|
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
|
||||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
|
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
|
||||||
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
|
Gender *string `json:"gender" bson:"gender" cql:"gender"`
|
||||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
|
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
|
||||||
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
|
||||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
|
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
|
||||||
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
|
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
|
||||||
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
|
Roles string `json:"roles" bson:"roles" cql:"roles"`
|
||||||
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp" dynamo:"revoked_timestamp"`
|
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
AppData *string `json:"app_data" bson:"app_data" cql:"app_data" dynamo:"app_data"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) AsAPIUser() *model.User {
|
func (user *User) AsAPIUser() *model.User {
|
||||||
isEmailVerified := user.EmailVerifiedAt != nil
|
isEmailVerified := user.EmailVerifiedAt != nil
|
||||||
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
||||||
appDataMap := make(map[string]interface{})
|
|
||||||
json.Unmarshal([]byte(refs.StringValue(user.AppData)), &appDataMap)
|
|
||||||
// id := user.ID
|
// id := user.ID
|
||||||
// if strings.Contains(id, Collections.User+"/") {
|
// if strings.Contains(id, Collections.User+"/") {
|
||||||
// id = strings.TrimPrefix(id, Collections.User+"/")
|
// id = strings.TrimPrefix(id, Collections.User+"/")
|
||||||
|
@ -54,7 +51,7 @@ func (user *User) AsAPIUser() *model.User {
|
||||||
FamilyName: user.FamilyName,
|
FamilyName: user.FamilyName,
|
||||||
MiddleName: user.MiddleName,
|
MiddleName: user.MiddleName,
|
||||||
Nickname: user.Nickname,
|
Nickname: user.Nickname,
|
||||||
PreferredUsername: user.Email,
|
PreferredUsername: refs.NewStringRef(user.Email),
|
||||||
Gender: user.Gender,
|
Gender: user.Gender,
|
||||||
Birthdate: user.Birthdate,
|
Birthdate: user.Birthdate,
|
||||||
PhoneNumber: user.PhoneNumber,
|
PhoneNumber: user.PhoneNumber,
|
||||||
|
@ -65,13 +62,5 @@ func (user *User) AsAPIUser() *model.User {
|
||||||
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
|
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
|
||||||
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
|
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
|
||||||
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
|
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
|
||||||
AppData: appDataMap,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) ToMap() map[string]interface{} {
|
|
||||||
res := map[string]interface{}{}
|
|
||||||
data, _ := json.Marshal(user) // Convert to a json string
|
|
||||||
json.Unmarshal(data, &res) // Convert to a map
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,16 +11,16 @@ import (
|
||||||
|
|
||||||
// VerificationRequest model for db
|
// VerificationRequest model for db
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
Token string `json:"token" bson:"token" cql:"jwt_token" dynamo:"token" index:"token,hash"`
|
Token string `gorm:"type:text" json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra
|
||||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier" dynamo:"identifier"`
|
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email" dynamo:"email"`
|
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"`
|
||||||
Nonce string `json:"nonce" bson:"nonce" cql:"nonce" dynamo:"nonce"`
|
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce" cql:"nonce"`
|
||||||
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri" dynamo:"redirect_uri"`
|
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||||
|
|
|
@ -10,42 +10,35 @@ import (
|
||||||
|
|
||||||
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
||||||
|
|
||||||
// Event name has been kept unique as per initial design. But later on decided that we can have
|
|
||||||
// multiple hooks for same event so will be in a pattern `event_name-TIMESTAMP`
|
|
||||||
|
|
||||||
// Webhook model for db
|
// Webhook model for db
|
||||||
type Webhook struct {
|
type Webhook struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
|
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||||
EventDescription string `json:"event_description" bson:"event_description" cql:"event_description" dynamo:"event_description"`
|
EndPoint string `gorm:"type:text" json:"endpoint" bson:"endpoint" cql:"endpoint"`
|
||||||
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint" dynamo:"endpoint"`
|
Headers string `gorm:"type:text" json:"headers" bson:"headers" cql:"headers"`
|
||||||
Headers string `json:"headers" bson:"headers" cql:"headers" dynamo:"headers"`
|
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled"`
|
||||||
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsAPIWebhook to return webhook as graphql response object
|
// AsAPIWebhook to return webhook as graphql response object
|
||||||
func (w *Webhook) AsAPIWebhook() *model.Webhook {
|
func (w *Webhook) AsAPIWebhook() *model.Webhook {
|
||||||
headersMap := make(map[string]interface{})
|
headersMap := make(map[string]interface{})
|
||||||
json.Unmarshal([]byte(w.Headers), &headersMap)
|
json.Unmarshal([]byte(w.Headers), &headersMap)
|
||||||
|
|
||||||
id := w.ID
|
id := w.ID
|
||||||
if strings.Contains(id, Collections.Webhook+"/") {
|
if strings.Contains(id, Collections.Webhook+"/") {
|
||||||
id = strings.TrimPrefix(id, Collections.Webhook+"/")
|
id = strings.TrimPrefix(id, Collections.Webhook+"/")
|
||||||
}
|
}
|
||||||
// set default title to event name without dot(.)
|
|
||||||
if w.EventDescription == "" {
|
|
||||||
w.EventDescription = strings.Join(strings.Split(w.EventName, "."), " ")
|
|
||||||
}
|
|
||||||
return &model.Webhook{
|
return &model.Webhook{
|
||||||
ID: id,
|
ID: id,
|
||||||
EventName: refs.NewStringRef(w.EventName),
|
EventName: refs.NewStringRef(w.EventName),
|
||||||
EventDescription: refs.NewStringRef(w.EventDescription),
|
Endpoint: refs.NewStringRef(w.EndPoint),
|
||||||
Endpoint: refs.NewStringRef(w.EndPoint),
|
Headers: headersMap,
|
||||||
Headers: headersMap,
|
Enabled: refs.NewBoolRef(w.Enabled),
|
||||||
Enabled: refs.NewBoolRef(w.Enabled),
|
CreatedAt: refs.NewInt64Ref(w.CreatedAt),
|
||||||
CreatedAt: refs.NewInt64Ref(w.CreatedAt),
|
UpdatedAt: refs.NewInt64Ref(w.UpdatedAt),
|
||||||
UpdatedAt: refs.NewInt64Ref(w.UpdatedAt),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,14 @@ import (
|
||||||
|
|
||||||
// WebhookLog model for db
|
// WebhookLog model for db
|
||||||
type WebhookLog struct {
|
type WebhookLog struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status" dynamo:"http_status"`
|
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status"`
|
||||||
Response string `json:"response" bson:"response" cql:"response" dynamo:"response"`
|
Response string `gorm:"type:text" json:"response" bson:"response" cql:"response"`
|
||||||
Request string `json:"request" bson:"request" cql:"request" dynamo:"request"`
|
Request string `gorm:"type:text" json:"request" bson:"request" cql:"request"`
|
||||||
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id" dynamo:"webhook_id" index:"webhook_id,hash"`
|
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsAPIWebhookLog to return webhook log as graphql response object
|
// AsAPIWebhookLog to return webhook log as graphql response object
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
package arangodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
arangoDriver "github.com/arangodb/go-driver"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
|
||||||
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
|
||||||
if exists != nil {
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
||||||
if authenticators.ID == "" {
|
|
||||||
authenticators.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticators.Key = authenticators.ID
|
|
||||||
authenticators.CreatedAt = time.Now().Unix()
|
|
||||||
authenticators.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
authenticatorsCollection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
|
|
||||||
meta, err := authenticatorsCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), authenticators)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
authenticators.Key = meta.Key
|
|
||||||
authenticators.ID = meta.ID.String()
|
|
||||||
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
|
||||||
authenticators.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
collection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
|
|
||||||
meta, err := collection.UpdateDocument(ctx, authenticators.Key, authenticators)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticators.Key = meta.Key
|
|
||||||
authenticators.ID = meta.ID.String()
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
|
||||||
var authenticators *models.Authenticator
|
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.user_id == @user_id AND d.method == @method LIMIT 1 RETURN d", models.Collections.Authenticators)
|
|
||||||
bindVars := map[string]interface{}{
|
|
||||||
"user_id": userId,
|
|
||||||
"method": authenticatorType,
|
|
||||||
}
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cursor.Close()
|
|
||||||
for {
|
|
||||||
if !cursor.HasMore() {
|
|
||||||
if authenticators == nil {
|
|
||||||
return authenticators, fmt.Errorf("authenticator not found")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
_, err := cursor.ReadDocument(ctx, &authenticators)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/arangodb/go-driver"
|
||||||
arangoDriver "github.com/arangodb/go-driver"
|
arangoDriver "github.com/arangodb/go-driver"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -12,14 +13,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddEmailTemplate to add EmailTemplate
|
// AddEmailTemplate to add EmailTemplate
|
||||||
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) {
|
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
|
||||||
if emailTemplate.ID == "" {
|
if emailTemplate.ID == "" {
|
||||||
emailTemplate.ID = uuid.New().String()
|
emailTemplate.ID = uuid.New().String()
|
||||||
emailTemplate.Key = emailTemplate.ID
|
emailTemplate.Key = emailTemplate.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
emailTemplate.Key = emailTemplate.ID
|
emailTemplate.Key = emailTemplate.ID
|
||||||
emailTemplate.CreatedAt = time.Now().Unix()
|
emailTemplate.CreatedAt = time.Now().Unix()
|
||||||
emailTemplate.UpdatedAt = time.Now().Unix()
|
emailTemplate.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate)
|
emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate)
|
||||||
_, err := emailTemplateCollection.CreateDocument(ctx, emailTemplate)
|
_, err := emailTemplateCollection.CreateDocument(ctx, emailTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -29,63 +32,74 @@ func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate *models.E
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateEmailTemplate to update EmailTemplate
|
// UpdateEmailTemplate to update EmailTemplate
|
||||||
func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) {
|
func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
|
||||||
emailTemplate.UpdatedAt = time.Now().Unix()
|
emailTemplate.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate)
|
emailTemplateCollection, _ := p.db.Collection(ctx, models.Collections.EmailTemplate)
|
||||||
meta, err := emailTemplateCollection.UpdateDocument(ctx, emailTemplate.Key, emailTemplate)
|
meta, err := emailTemplateCollection.UpdateDocument(ctx, emailTemplate.Key, emailTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
emailTemplate.Key = meta.Key
|
emailTemplate.Key = meta.Key
|
||||||
emailTemplate.ID = meta.ID.String()
|
emailTemplate.ID = meta.ID.String()
|
||||||
return emailTemplate.AsAPIEmailTemplate(), nil
|
return emailTemplate.AsAPIEmailTemplate(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListEmailTemplates to list EmailTemplate
|
// ListEmailTemplates to list EmailTemplate
|
||||||
func (p *provider) ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error) {
|
func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagination) (*model.EmailTemplates, error) {
|
||||||
emailTemplates := []*model.EmailTemplate{}
|
emailTemplates := []*model.EmailTemplate{}
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.EmailTemplate, pagination.Offset, pagination.Limit)
|
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.EmailTemplate, pagination.Offset, pagination.Limit)
|
||||||
sctx := arangoDriver.WithQueryFullCount(ctx)
|
|
||||||
|
sctx := driver.WithQueryFullCount(ctx)
|
||||||
cursor, err := p.db.Query(sctx, query, nil)
|
cursor, err := p.db.Query(sctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
paginationClone.Total = cursor.Statistics().FullCount()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var emailTemplate *models.EmailTemplate
|
var emailTemplate models.EmailTemplate
|
||||||
meta, err := cursor.ReadDocument(ctx, &emailTemplate)
|
meta, err := cursor.ReadDocument(ctx, &emailTemplate)
|
||||||
|
|
||||||
if arangoDriver.IsNoMoreDocuments(err) {
|
if arangoDriver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.Key != "" {
|
if meta.Key != "" {
|
||||||
emailTemplates = append(emailTemplates, emailTemplate.AsAPIEmailTemplate())
|
emailTemplates = append(emailTemplates, emailTemplate.AsAPIEmailTemplate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.EmailTemplates{
|
return &model.EmailTemplates{
|
||||||
Pagination: paginationClone,
|
Pagination: &paginationClone,
|
||||||
EmailTemplates: emailTemplates,
|
EmailTemplates: emailTemplates,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEmailTemplateByID to get EmailTemplate by id
|
// GetEmailTemplateByID to get EmailTemplate by id
|
||||||
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
|
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
|
||||||
var emailTemplate *models.EmailTemplate
|
var emailTemplate models.EmailTemplate
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d._key == @email_template_id RETURN d", models.Collections.EmailTemplate)
|
query := fmt.Sprintf("FOR d in %s FILTER d._key == @email_template_id RETURN d", models.Collections.EmailTemplate)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"email_template_id": emailTemplateID,
|
"email_template_id": emailTemplateID,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if emailTemplate == nil {
|
if emailTemplate.Key == "" {
|
||||||
return nil, fmt.Errorf("email template not found")
|
return nil, fmt.Errorf("email template not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -100,19 +114,21 @@ func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID str
|
||||||
|
|
||||||
// GetEmailTemplateByEventName to get EmailTemplate by event_name
|
// GetEmailTemplateByEventName to get EmailTemplate by event_name
|
||||||
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
|
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
|
||||||
var emailTemplate *models.EmailTemplate
|
var emailTemplate models.EmailTemplate
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.event_name == @event_name RETURN d", models.Collections.EmailTemplate)
|
query := fmt.Sprintf("FOR d in %s FILTER d.event_name == @event_name RETURN d", models.Collections.EmailTemplate)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"event_name": eventName,
|
"event_name": eventName,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if emailTemplate == nil {
|
if emailTemplate.Key == "" {
|
||||||
return nil, fmt.Errorf("email template not found")
|
return nil, fmt.Errorf("email template not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddEnv to save environment information in database
|
// AddEnv to save environment information in database
|
||||||
func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, error) {
|
func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
||||||
if env.ID == "" {
|
if env.ID == "" {
|
||||||
env.ID = uuid.New().String()
|
env.ID = uuid.New().String()
|
||||||
env.Key = env.ID
|
env.Key = env.ID
|
||||||
|
@ -23,7 +23,7 @@ func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, er
|
||||||
configCollection, _ := p.db.Collection(ctx, models.Collections.Env)
|
configCollection, _ := p.db.Collection(ctx, models.Collections.Env)
|
||||||
meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), env)
|
meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return env, err
|
||||||
}
|
}
|
||||||
env.Key = meta.Key
|
env.Key = meta.Key
|
||||||
env.ID = meta.ID.String()
|
env.ID = meta.ID.String()
|
||||||
|
@ -31,12 +31,12 @@ func (p *provider) AddEnv(ctx context.Context, env *models.Env) (*models.Env, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateEnv to update environment information in database
|
// UpdateEnv to update environment information in database
|
||||||
func (p *provider) UpdateEnv(ctx context.Context, env *models.Env) (*models.Env, error) {
|
func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
||||||
env.UpdatedAt = time.Now().Unix()
|
env.UpdatedAt = time.Now().Unix()
|
||||||
collection, _ := p.db.Collection(ctx, models.Collections.Env)
|
collection, _ := p.db.Collection(ctx, models.Collections.Env)
|
||||||
meta, err := collection.UpdateDocument(ctx, env.Key, env)
|
meta, err := collection.UpdateDocument(ctx, env.Key, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return env, err
|
||||||
}
|
}
|
||||||
|
|
||||||
env.Key = meta.Key
|
env.Key = meta.Key
|
||||||
|
@ -45,24 +45,26 @@ func (p *provider) UpdateEnv(ctx context.Context, env *models.Env) (*models.Env,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnv to get environment information from database
|
// GetEnv to get environment information from database
|
||||||
func (p *provider) GetEnv(ctx context.Context) (*models.Env, error) {
|
func (p *provider) GetEnv(ctx context.Context) (models.Env, error) {
|
||||||
var env *models.Env
|
var env models.Env
|
||||||
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.Env)
|
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.Env)
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, nil)
|
cursor, err := p.db.Query(ctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return env, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if env == nil {
|
if env.Key == "" {
|
||||||
return env, fmt.Errorf("config not found")
|
return env, fmt.Errorf("config not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err := cursor.ReadDocument(ctx, &env)
|
_, err := cursor.ReadDocument(ctx, &env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return env, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package arangodb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -13,39 +12,27 @@ import (
|
||||||
|
|
||||||
// UpsertOTP to add or update otp
|
// UpsertOTP to add or update otp
|
||||||
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
|
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
|
||||||
// check if email or phone number is present
|
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
|
||||||
if otpParam.Email == "" && otpParam.PhoneNumber == "" {
|
|
||||||
return nil, errors.New("email or phone_number is required")
|
|
||||||
}
|
|
||||||
uniqueField := models.FieldNameEmail
|
|
||||||
if otpParam.Email == "" && otpParam.PhoneNumber != "" {
|
|
||||||
uniqueField = models.FieldNamePhoneNumber
|
|
||||||
}
|
|
||||||
var otp *models.OTP
|
|
||||||
if uniqueField == models.FieldNameEmail {
|
|
||||||
otp, _ = p.GetOTPByEmail(ctx, otpParam.Email)
|
|
||||||
} else {
|
|
||||||
otp, _ = p.GetOTPByPhoneNumber(ctx, otpParam.PhoneNumber)
|
|
||||||
}
|
|
||||||
shouldCreate := false
|
shouldCreate := false
|
||||||
if otp == nil {
|
if otp == nil {
|
||||||
id := uuid.NewString()
|
id := uuid.NewString()
|
||||||
otp = &models.OTP{
|
otp = &models.OTP{
|
||||||
ID: id,
|
ID: id,
|
||||||
Key: id,
|
Key: id,
|
||||||
Otp: otpParam.Otp,
|
Otp: otpParam.Otp,
|
||||||
Email: otpParam.Email,
|
Email: otpParam.Email,
|
||||||
PhoneNumber: otpParam.PhoneNumber,
|
ExpiresAt: otpParam.ExpiresAt,
|
||||||
ExpiresAt: otpParam.ExpiresAt,
|
CreatedAt: time.Now().Unix(),
|
||||||
CreatedAt: time.Now().Unix(),
|
|
||||||
}
|
}
|
||||||
shouldCreate = true
|
shouldCreate = true
|
||||||
} else {
|
} else {
|
||||||
otp.Otp = otpParam.Otp
|
otp.Otp = otpParam.Otp
|
||||||
otp.ExpiresAt = otpParam.ExpiresAt
|
otp.ExpiresAt = otpParam.ExpiresAt
|
||||||
}
|
}
|
||||||
|
|
||||||
otp.UpdatedAt = time.Now().Unix()
|
otp.UpdatedAt = time.Now().Unix()
|
||||||
otpCollection, _ := p.db.Collection(ctx, models.Collections.OTP)
|
otpCollection, _ := p.db.Collection(ctx, models.Collections.OTP)
|
||||||
|
|
||||||
var meta driver.DocumentMeta
|
var meta driver.DocumentMeta
|
||||||
var err error
|
var err error
|
||||||
if shouldCreate {
|
if shouldCreate {
|
||||||
|
@ -53,9 +40,11 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
|
||||||
} else {
|
} else {
|
||||||
meta, err = otpCollection.UpdateDocument(ctx, otp.Key, otp)
|
meta, err = otpCollection.UpdateDocument(ctx, otp.Key, otp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
otp.Key = meta.Key
|
otp.Key = meta.Key
|
||||||
otp.ID = meta.ID.String()
|
otp.ID = meta.ID.String()
|
||||||
return otp, nil
|
return otp, nil
|
||||||
|
@ -63,47 +52,22 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
|
||||||
|
|
||||||
// GetOTPByEmail to get otp for a given email address
|
// GetOTPByEmail to get otp for a given email address
|
||||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
||||||
var otp *models.OTP
|
var otp models.OTP
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.OTP)
|
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.OTP)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"email": emailAddress,
|
"email": emailAddress,
|
||||||
}
|
}
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cursor.Close()
|
|
||||||
for {
|
|
||||||
if !cursor.HasMore() {
|
|
||||||
if otp == nil {
|
|
||||||
return nil, fmt.Errorf("otp with given email not found")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
_, err := cursor.ReadDocument(ctx, &otp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return otp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOTPByPhoneNumber to get otp for a given phone number
|
|
||||||
func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) {
|
|
||||||
var otp *models.OTP
|
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.OTP)
|
|
||||||
bindVars := map[string]interface{}{
|
|
||||||
"phone_number": phoneNumber,
|
|
||||||
}
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if otp == nil {
|
if otp.Key == "" {
|
||||||
return nil, fmt.Errorf("otp with given phone_number not found")
|
return nil, fmt.Errorf("email template not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -112,7 +76,8 @@ func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return otp, nil
|
|
||||||
|
return &otp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOTP to delete otp
|
// DeleteOTP to delete otp
|
||||||
|
@ -122,5 +87,6 @@ func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,8 @@ package arangodb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
|
"github.com/arangodb/go-driver"
|
||||||
arangoDriver "github.com/arangodb/go-driver"
|
arangoDriver "github.com/arangodb/go-driver"
|
||||||
"github.com/arangodb/go-driver/http"
|
"github.com/arangodb/go-driver/http"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
@ -25,73 +22,44 @@ type provider struct {
|
||||||
func NewProvider() (*provider, error) {
|
func NewProvider() (*provider, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
||||||
dbUsername := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseUsername
|
conn, err := http.NewConnection(http.ConnectionConfig{
|
||||||
dbPassword := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabasePassword
|
|
||||||
dbCACertificate := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseCACert
|
|
||||||
httpConfig := http.ConnectionConfig{
|
|
||||||
Endpoints: []string{dbURL},
|
Endpoints: []string{dbURL},
|
||||||
}
|
})
|
||||||
// If ca certificate if present, create tls config
|
|
||||||
if dbCACertificate != "" {
|
|
||||||
caCert, err := base64.StdEncoding.DecodeString(dbCACertificate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Prepare TLS Config
|
|
||||||
tlsConfig := &tls.Config{}
|
|
||||||
certPool := x509.NewCertPool()
|
|
||||||
if success := certPool.AppendCertsFromPEM(caCert); !success {
|
|
||||||
return nil, fmt.Errorf("invalid certificate")
|
|
||||||
}
|
|
||||||
tlsConfig.RootCAs = certPool
|
|
||||||
httpConfig.TLSConfig = tlsConfig
|
|
||||||
}
|
|
||||||
// Create new http connection
|
|
||||||
conn, err := http.NewConnection(httpConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
clientConfig := arangoDriver.ClientConfig{
|
|
||||||
|
arangoClient, err := arangoDriver.NewClient(arangoDriver.ClientConfig{
|
||||||
Connection: conn,
|
Connection: conn,
|
||||||
}
|
})
|
||||||
if dbUsername != "" && dbPassword != "" {
|
|
||||||
clientConfig.Authentication = arangoDriver.BasicAuthentication(dbUsername, dbPassword)
|
|
||||||
}
|
|
||||||
arangoClient, err := arangoDriver.NewClient(clientConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var arangodb arangoDriver.Database
|
|
||||||
|
var arangodb driver.Database
|
||||||
dbName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseName
|
dbName := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseName
|
||||||
arangodb_exists, err := arangoClient.DatabaseExists(ctx, dbName)
|
arangodb_exists, err := arangoClient.DatabaseExists(nil, dbName)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if arangodb_exists {
|
if arangodb_exists {
|
||||||
arangodb, err = arangoClient.Database(ctx, dbName)
|
arangodb, err = arangoClient.Database(nil, dbName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arangodb, err = arangoClient.CreateDatabase(ctx, dbName, nil)
|
arangodb, err = arangoClient.CreateDatabase(nil, dbName, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.User)
|
userCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.User)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !userCollectionExists {
|
if !userCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.User, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.User, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userCollection, err := arangodb.Collection(ctx, models.Collections.User)
|
userCollection, _ := arangodb.Collection(nil, models.Collections.User)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
|
userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
|
@ -102,19 +70,14 @@ func NewProvider() (*provider, error) {
|
||||||
})
|
})
|
||||||
|
|
||||||
verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.VerificationRequest)
|
verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.VerificationRequest)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !verificationRequestCollectionExists {
|
if !verificationRequestCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.VerificationRequest, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.VerificationRequest, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
verificationRequestCollection, err := arangodb.Collection(ctx, models.Collections.VerificationRequest)
|
|
||||||
if err != nil {
|
verificationRequestCollection, _ := arangodb.Collection(nil, models.Collections.VerificationRequest)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{
|
verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
|
@ -124,123 +87,78 @@ func NewProvider() (*provider, error) {
|
||||||
})
|
})
|
||||||
|
|
||||||
sessionCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Session)
|
sessionCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Session)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !sessionCollectionExists {
|
if !sessionCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.Session, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.Session, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sessionCollection, err := arangodb.Collection(ctx, models.Collections.Session)
|
|
||||||
if err != nil {
|
sessionCollection, _ := arangodb.Collection(nil, models.Collections.Session)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sessionCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
|
sessionCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
envCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env)
|
|
||||||
if err != nil {
|
configCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env)
|
||||||
return nil, err
|
if !configCollectionExists {
|
||||||
}
|
|
||||||
if !envCollectionExists {
|
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.Env, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.Env, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
webhookCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Webhook)
|
webhookCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Webhook)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !webhookCollectionExists {
|
if !webhookCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.Webhook, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.Webhook, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
webhookCollection, err := arangodb.Collection(ctx, models.Collections.Webhook)
|
|
||||||
if err != nil {
|
webhookCollection, _ := arangodb.Collection(nil, models.Collections.Webhook)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
webhookCollection.EnsureHashIndex(ctx, []string{"event_name"}, &arangoDriver.EnsureHashIndexOptions{
|
webhookCollection.EnsureHashIndex(ctx, []string{"event_name"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
webhookLogCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.WebhookLog)
|
webhookLogCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.WebhookLog)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !webhookLogCollectionExists {
|
if !webhookLogCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.WebhookLog, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.WebhookLog, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
webhookLogCollection, err := arangodb.Collection(ctx, models.Collections.WebhookLog)
|
|
||||||
if err != nil {
|
webhookLogCollection, _ := arangodb.Collection(nil, models.Collections.WebhookLog)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{
|
webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate)
|
emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !emailTemplateCollectionExists {
|
if !emailTemplateCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.EmailTemplate, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.EmailTemplate, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emailTemplateCollection, err := arangodb.Collection(ctx, models.Collections.EmailTemplate)
|
|
||||||
if err != nil {
|
emailTemplateCollection, _ := arangodb.Collection(nil, models.Collections.EmailTemplate)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
emailTemplateCollection.EnsureHashIndex(ctx, []string{"event_name"}, &arangoDriver.EnsureHashIndexOptions{
|
emailTemplateCollection.EnsureHashIndex(ctx, []string{"event_name"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
|
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !otpCollectionExists {
|
if !otpCollectionExists {
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.OTP, nil)
|
_, err = arangodb.CreateCollection(ctx, models.Collections.OTP, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
otpCollection, err := arangodb.Collection(ctx, models.Collections.OTP)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
otpCollection.EnsureHashIndex(ctx, []string{models.FieldNameEmail, models.FieldNamePhoneNumber}, &arangoDriver.EnsureHashIndexOptions{
|
|
||||||
Unique: true,
|
|
||||||
Sparse: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
//authenticators table define
|
otpCollection, _ := arangodb.Collection(nil, models.Collections.OTP)
|
||||||
authenticatorsCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Authenticators)
|
otpCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
if err != nil {
|
Unique: true,
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !authenticatorsCollectionExists {
|
|
||||||
_, err = arangodb.CreateCollection(ctx, models.Collections.Authenticators, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authenticatorsCollection, err := arangodb.Collection(ctx, models.Collections.Authenticators)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
authenticatorsCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
|
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSession to save session information in database
|
// AddSession to save session information in database
|
||||||
func (p *provider) AddSession(ctx context.Context, session *models.Session) error {
|
func (p *provider) AddSession(ctx context.Context, session models.Session) error {
|
||||||
if session.ID == "" {
|
if session.ID == "" {
|
||||||
session.ID = uuid.New().String()
|
session.ID = uuid.New().String()
|
||||||
session.Key = session.ID
|
session.Key = session.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
session.CreatedAt = time.Now().Unix()
|
session.CreatedAt = time.Now().Unix()
|
||||||
session.UpdatedAt = time.Now().Unix()
|
session.UpdatedAt = time.Now().Unix()
|
||||||
sessionCollection, _ := p.db.Collection(ctx, models.Collections.Session)
|
sessionCollection, _ := p.db.Collection(ctx, models.Collections.Session)
|
||||||
|
@ -23,8 +24,3 @@ func (p *provider) AddSession(ctx context.Context, session *models.Session) erro
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSession to delete session information from database
|
|
||||||
func (p *provider) DeleteSession(ctx context.Context, userId string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/arangodb/go-driver"
|
||||||
arangoDriver "github.com/arangodb/go-driver"
|
arangoDriver "github.com/arangodb/go-driver"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
@ -14,11 +15,10 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/refs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddUser to save user information in database
|
// AddUser to save user information in database
|
||||||
func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User, error) {
|
func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, error) {
|
||||||
if user.ID == "" {
|
if user.ID == "" {
|
||||||
user.ID = uuid.New().String()
|
user.ID = uuid.New().String()
|
||||||
user.Key = user.ID
|
user.Key = user.ID
|
||||||
|
@ -27,27 +27,17 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
|
||||||
if user.Roles == "" {
|
if user.Roles == "" {
|
||||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
user.Roles = defaultRoles
|
user.Roles = defaultRoles
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
|
|
||||||
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
|
|
||||||
return user, fmt.Errorf("user with given phone number already exists")
|
|
||||||
}
|
|
||||||
} else if user.Email != nil && strings.TrimSpace(refs.StringValue(user.Email)) != "" {
|
|
||||||
if u, _ := p.GetUserByEmail(ctx, refs.StringValue(user.Email)); u != nil && u.ID != user.ID {
|
|
||||||
return user, fmt.Errorf("user with given email already exists")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user.CreatedAt = time.Now().Unix()
|
user.CreatedAt = time.Now().Unix()
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
userCollection, _ := p.db.Collection(ctx, models.Collections.User)
|
userCollection, _ := p.db.Collection(ctx, models.Collections.User)
|
||||||
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), user)
|
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
user.Key = meta.Key
|
user.Key = meta.Key
|
||||||
user.ID = meta.ID.String()
|
user.ID = meta.ID.String()
|
||||||
|
@ -56,13 +46,12 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser to update user information in database
|
// UpdateUser to update user information in database
|
||||||
func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.User, error) {
|
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
collection, _ := p.db.Collection(ctx, models.Collections.User)
|
collection, _ := p.db.Collection(ctx, models.Collections.User)
|
||||||
meta, err := collection.UpdateDocument(ctx, user.Key, user)
|
meta, err := collection.UpdateDocument(ctx, user.Key, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Key = meta.Key
|
user.Key = meta.Key
|
||||||
|
@ -71,12 +60,13 @@ func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.U
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteUser to delete user information from database
|
// DeleteUser to delete user information from database
|
||||||
func (p *provider) DeleteUser(ctx context.Context, user *models.User) error {
|
func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
|
||||||
collection, _ := p.db.Collection(ctx, models.Collections.User)
|
collection, _ := p.db.Collection(ctx, models.Collections.User)
|
||||||
_, err := collection.RemoveDocument(ctx, user.Key)
|
_, err := collection.RemoveDocument(ctx, user.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @user_id REMOVE { _key: d._key } IN %s`, models.Collections.Session, models.Collections.Session)
|
query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @user_id REMOVE { _key: d._key } IN %s`, models.Collections.Session, models.Collections.Session)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"user_id": user.Key,
|
"user_id": user.Key,
|
||||||
|
@ -86,91 +76,106 @@ func (p *provider) DeleteUser(ctx context.Context, user *models.User) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListUsers to get list of users from database
|
// ListUsers to get list of users from database
|
||||||
func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination) (*model.Users, error) {
|
func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error) {
|
||||||
var users []*model.User
|
var users []*model.User
|
||||||
sctx := arangoDriver.WithQueryFullCount(ctx)
|
sctx := driver.WithQueryFullCount(ctx)
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit)
|
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit)
|
||||||
|
|
||||||
cursor, err := p.db.Query(sctx, query, nil)
|
cursor, err := p.db.Query(sctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
paginationClone.Total = cursor.Statistics().FullCount()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var user *models.User
|
var user models.User
|
||||||
meta, err := cursor.ReadDocument(ctx, &user)
|
meta, err := cursor.ReadDocument(ctx, &user)
|
||||||
|
|
||||||
if arangoDriver.IsNoMoreDocuments(err) {
|
if arangoDriver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.Key != "" {
|
if meta.Key != "" {
|
||||||
users = append(users, user.AsAPIUser())
|
users = append(users, user.AsAPIUser())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.Users{
|
return &model.Users{
|
||||||
Pagination: paginationClone,
|
Pagination: &paginationClone,
|
||||||
Users: users,
|
Users: users,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
func (p *provider) GetUserByEmail(ctx context.Context, email string) (models.User, error) {
|
||||||
var user *models.User
|
var user models.User
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.User)
|
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.User)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"email": email,
|
"email": email,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if user == nil {
|
if user.Key == "" {
|
||||||
return nil, fmt.Errorf("user not found")
|
return user, fmt.Errorf("user not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err := cursor.ReadDocument(ctx, &user)
|
_, err := cursor.ReadDocument(ctx, &user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByID to get user information from database using user ID
|
// GetUserByID to get user information from database using user ID
|
||||||
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
|
func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, error) {
|
||||||
var user *models.User
|
var user models.User
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d", models.Collections.User)
|
query := fmt.Sprintf("FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d", models.Collections.User)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"id": id,
|
"id": id,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if user == nil {
|
if user.Key == "" {
|
||||||
return nil, fmt.Errorf("user not found")
|
return user, fmt.Errorf("user not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err := cursor.ReadDocument(ctx, &user)
|
_, err := cursor.ReadDocument(ctx, &user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return user, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,12 +184,14 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, er
|
||||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
||||||
// set updated_at time for all users
|
// set updated_at time for all users
|
||||||
data["updated_at"] = time.Now().Unix()
|
data["updated_at"] = time.Now().Unix()
|
||||||
|
|
||||||
userInfoBytes, err := json.Marshal(data)
|
userInfoBytes, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := ""
|
query := ""
|
||||||
if len(ids) > 0 {
|
if ids != nil && len(ids) > 0 {
|
||||||
keysArray := ""
|
keysArray := ""
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
keysArray += fmt.Sprintf("'%s', ", id)
|
keysArray += fmt.Sprintf("'%s', ", id)
|
||||||
|
@ -195,36 +202,12 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
|
||||||
} else {
|
} else {
|
||||||
query = fmt.Sprintf("FOR u IN %s UPDATE u._key with %s IN %s", models.Collections.User, string(userInfoBytes), models.Collections.User)
|
query = fmt.Sprintf("FOR u IN %s UPDATE u._key with %s IN %s", models.Collections.User, string(userInfoBytes), models.Collections.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = p.db.Query(ctx, query, nil)
|
_, err = p.db.Query(ctx, query, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByPhoneNumber to get user information from database using phone number
|
|
||||||
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
|
|
||||||
var user *models.User
|
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.User)
|
|
||||||
bindVars := map[string]interface{}{
|
|
||||||
"phone_number": phoneNumber,
|
|
||||||
}
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cursor.Close()
|
|
||||||
for {
|
|
||||||
if !cursor.HasMore() {
|
|
||||||
if user == nil {
|
|
||||||
return nil, fmt.Errorf("user not found")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
_, err := cursor.ReadDocument(ctx, &user)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,102 +5,114 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
arangoDriver "github.com/arangodb/go-driver"
|
"github.com/arangodb/go-driver"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddVerification to save verification request in database
|
// AddVerification to save verification request in database
|
||||||
func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) (*models.VerificationRequest, error) {
|
func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
|
||||||
if verificationRequest.ID == "" {
|
if verificationRequest.ID == "" {
|
||||||
verificationRequest.ID = uuid.New().String()
|
verificationRequest.ID = uuid.New().String()
|
||||||
verificationRequest.Key = verificationRequest.ID
|
verificationRequest.Key = verificationRequest.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
verificationRequest.CreatedAt = time.Now().Unix()
|
verificationRequest.CreatedAt = time.Now().Unix()
|
||||||
verificationRequest.UpdatedAt = time.Now().Unix()
|
verificationRequest.UpdatedAt = time.Now().Unix()
|
||||||
verificationRequestRequestCollection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest)
|
verificationRequestRequestCollection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest)
|
||||||
meta, err := verificationRequestRequestCollection.CreateDocument(ctx, verificationRequest)
|
meta, err := verificationRequestRequestCollection.CreateDocument(ctx, verificationRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequest, err
|
||||||
}
|
}
|
||||||
verificationRequest.Key = meta.Key
|
verificationRequest.Key = meta.Key
|
||||||
verificationRequest.ID = meta.ID.String()
|
verificationRequest.ID = meta.ID.String()
|
||||||
|
|
||||||
return verificationRequest, nil
|
return verificationRequest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVerificationRequestByToken to get verification request from database using token
|
// GetVerificationRequestByToken to get verification request from database using token
|
||||||
func (p *provider) GetVerificationRequestByToken(ctx context.Context, token string) (*models.VerificationRequest, error) {
|
func (p *provider) GetVerificationRequestByToken(ctx context.Context, token string) (models.VerificationRequest, error) {
|
||||||
var verificationRequest *models.VerificationRequest
|
var verificationRequest models.VerificationRequest
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.token == @token LIMIT 1 RETURN d", models.Collections.VerificationRequest)
|
query := fmt.Sprintf("FOR d in %s FILTER d.token == @token LIMIT 1 RETURN d", models.Collections.VerificationRequest)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"token": token,
|
"token": token,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequest, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if verificationRequest == nil {
|
if verificationRequest.Key == "" {
|
||||||
return verificationRequest, fmt.Errorf("verification request not found")
|
return verificationRequest, fmt.Errorf("verification request not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err := cursor.ReadDocument(ctx, &verificationRequest)
|
_, err := cursor.ReadDocument(ctx, &verificationRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequest, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return verificationRequest, nil
|
return verificationRequest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVerificationRequestByEmail to get verification request by email from database
|
// GetVerificationRequestByEmail to get verification request by email from database
|
||||||
func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email string, identifier string) (*models.VerificationRequest, error) {
|
func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email string, identifier string) (models.VerificationRequest, error) {
|
||||||
var verificationRequest *models.VerificationRequest
|
var verificationRequest models.VerificationRequest
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email FILTER d.identifier == @identifier LIMIT 1 RETURN d", models.Collections.VerificationRequest)
|
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email FILTER d.identifier == @identifier LIMIT 1 RETURN d", models.Collections.VerificationRequest)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"email": email,
|
"email": email,
|
||||||
"identifier": identifier,
|
"identifier": identifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequest, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if verificationRequest == nil {
|
if verificationRequest.Key == "" {
|
||||||
return verificationRequest, fmt.Errorf("verification request not found")
|
return verificationRequest, fmt.Errorf("verification request not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err := cursor.ReadDocument(ctx, &verificationRequest)
|
_, err := cursor.ReadDocument(ctx, &verificationRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return verificationRequest, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return verificationRequest, nil
|
return verificationRequest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListVerificationRequests to get list of verification requests from database
|
// ListVerificationRequests to get list of verification requests from database
|
||||||
func (p *provider) ListVerificationRequests(ctx context.Context, pagination *model.Pagination) (*model.VerificationRequests, error) {
|
func (p *provider) ListVerificationRequests(ctx context.Context, pagination model.Pagination) (*model.VerificationRequests, error) {
|
||||||
var verificationRequests []*model.VerificationRequest
|
var verificationRequests []*model.VerificationRequest
|
||||||
sctx := arangoDriver.WithQueryFullCount(ctx)
|
sctx := driver.WithQueryFullCount(ctx)
|
||||||
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.VerificationRequest, pagination.Offset, pagination.Limit)
|
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.VerificationRequest, pagination.Offset, pagination.Limit)
|
||||||
|
|
||||||
cursor, err := p.db.Query(sctx, query, nil)
|
cursor, err := p.db.Query(sctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
paginationClone.Total = cursor.Statistics().FullCount()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var verificationRequest *models.VerificationRequest
|
var verificationRequest models.VerificationRequest
|
||||||
meta, err := cursor.ReadDocument(ctx, &verificationRequest)
|
meta, err := cursor.ReadDocument(ctx, &verificationRequest)
|
||||||
|
|
||||||
if arangoDriver.IsNoMoreDocuments(err) {
|
if driver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -111,16 +123,17 @@ func (p *provider) ListVerificationRequests(ctx context.Context, pagination *mod
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.VerificationRequests{
|
return &model.VerificationRequests{
|
||||||
VerificationRequests: verificationRequests,
|
VerificationRequests: verificationRequests,
|
||||||
Pagination: paginationClone,
|
Pagination: &paginationClone,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteVerificationRequest to delete verification request from database
|
// DeleteVerificationRequest to delete verification request from database
|
||||||
func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) error {
|
func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) error {
|
||||||
collection, _ := p.db.Collection(ctx, models.Collections.VerificationRequest)
|
collection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
|
||||||
_, err := collection.RemoveDocument(ctx, verificationRequest.Key)
|
_, err := collection.RemoveDocument(nil, verificationRequest.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package arangodb
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/arangodb/go-driver"
|
"github.com/arangodb/go-driver"
|
||||||
|
@ -14,14 +13,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddWebhook to add webhook
|
// AddWebhook to add webhook
|
||||||
func (p *provider) AddWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error) {
|
func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
|
||||||
if webhook.ID == "" {
|
if webhook.ID == "" {
|
||||||
webhook.ID = uuid.New().String()
|
webhook.ID = uuid.New().String()
|
||||||
webhook.Key = webhook.ID
|
webhook.Key = webhook.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
webhook.Key = webhook.ID
|
webhook.Key = webhook.ID
|
||||||
// Add timestamp to make event name unique for legacy version
|
|
||||||
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
|
|
||||||
webhook.CreatedAt = time.Now().Unix()
|
webhook.CreatedAt = time.Now().Unix()
|
||||||
webhook.UpdatedAt = time.Now().Unix()
|
webhook.UpdatedAt = time.Now().Unix()
|
||||||
webhookCollection, _ := p.db.Collection(ctx, models.Collections.Webhook)
|
webhookCollection, _ := p.db.Collection(ctx, models.Collections.Webhook)
|
||||||
|
@ -33,37 +31,39 @@ func (p *provider) AddWebhook(ctx context.Context, webhook *models.Webhook) (*mo
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateWebhook to update webhook
|
// UpdateWebhook to update webhook
|
||||||
func (p *provider) UpdateWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error) {
|
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
|
||||||
webhook.UpdatedAt = time.Now().Unix()
|
webhook.UpdatedAt = time.Now().Unix()
|
||||||
// Event is changed
|
|
||||||
if !strings.Contains(webhook.EventName, "-") {
|
|
||||||
webhook.EventName = fmt.Sprintf("%s-%d", webhook.EventName, time.Now().Unix())
|
|
||||||
}
|
|
||||||
webhookCollection, _ := p.db.Collection(ctx, models.Collections.Webhook)
|
webhookCollection, _ := p.db.Collection(ctx, models.Collections.Webhook)
|
||||||
meta, err := webhookCollection.UpdateDocument(ctx, webhook.Key, webhook)
|
meta, err := webhookCollection.UpdateDocument(ctx, webhook.Key, webhook)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
webhook.Key = meta.Key
|
webhook.Key = meta.Key
|
||||||
webhook.ID = meta.ID.String()
|
webhook.ID = meta.ID.String()
|
||||||
return webhook.AsAPIWebhook(), nil
|
return webhook.AsAPIWebhook(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListWebhooks to list webhook
|
// ListWebhooks to list webhook
|
||||||
func (p *provider) ListWebhook(ctx context.Context, pagination *model.Pagination) (*model.Webhooks, error) {
|
func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination) (*model.Webhooks, error) {
|
||||||
webhooks := []*model.Webhook{}
|
webhooks := []*model.Webhook{}
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.Webhook, pagination.Offset, pagination.Limit)
|
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.Webhook, pagination.Offset, pagination.Limit)
|
||||||
sctx := arangoDriver.WithQueryFullCount(ctx)
|
|
||||||
|
sctx := driver.WithQueryFullCount(ctx)
|
||||||
cursor, err := p.db.Query(sctx, query, nil)
|
cursor, err := p.db.Query(sctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
paginationClone.Total = cursor.Statistics().FullCount()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var webhook *models.Webhook
|
var webhook models.Webhook
|
||||||
meta, err := cursor.ReadDocument(ctx, &webhook)
|
meta, err := cursor.ReadDocument(ctx, &webhook)
|
||||||
|
|
||||||
if arangoDriver.IsNoMoreDocuments(err) {
|
if arangoDriver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -76,26 +76,28 @@ func (p *provider) ListWebhook(ctx context.Context, pagination *model.Pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.Webhooks{
|
return &model.Webhooks{
|
||||||
Pagination: paginationClone,
|
Pagination: &paginationClone,
|
||||||
Webhooks: webhooks,
|
Webhooks: webhooks,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhookByID to get webhook by id
|
// GetWebhookByID to get webhook by id
|
||||||
func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) {
|
func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) {
|
||||||
var webhook *models.Webhook
|
var webhook models.Webhook
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d._key == @webhook_id RETURN d", models.Collections.Webhook)
|
query := fmt.Sprintf("FOR d in %s FILTER d._key == @webhook_id RETURN d", models.Collections.Webhook)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"webhook_id": webhookID,
|
"webhook_id": webhookID,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !cursor.HasMore() {
|
if !cursor.HasMore() {
|
||||||
if webhook == nil {
|
if webhook.Key == "" {
|
||||||
return nil, fmt.Errorf("webhook not found")
|
return nil, fmt.Errorf("webhook not found")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -109,28 +111,32 @@ func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhookByEventName to get webhook by event_name
|
// GetWebhookByEventName to get webhook by event_name
|
||||||
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) ([]*model.Webhook, error) {
|
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
|
||||||
query := fmt.Sprintf("FOR d in %s FILTER d.event_name LIKE @event_name RETURN d", models.Collections.Webhook)
|
var webhook models.Webhook
|
||||||
|
query := fmt.Sprintf("FOR d in %s FILTER d.event_name == @event_name RETURN d", models.Collections.Webhook)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"event_name": eventName + "%",
|
"event_name": eventName,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
webhooks := []*model.Webhook{}
|
|
||||||
for {
|
for {
|
||||||
var webhook *models.Webhook
|
if !cursor.HasMore() {
|
||||||
if _, err := cursor.ReadDocument(ctx, &webhook); driver.IsNoMoreDocuments(err) {
|
if webhook.Key == "" {
|
||||||
// We're done
|
return nil, fmt.Errorf("webhook not found")
|
||||||
|
}
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
}
|
||||||
|
_, err := cursor.ReadDocument(ctx, &webhook)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
webhooks = append(webhooks, webhook.AsAPIWebhook())
|
|
||||||
}
|
}
|
||||||
return webhooks, nil
|
return webhook.AsAPIWebhook(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteWebhook to delete webhook
|
// DeleteWebhook to delete webhook
|
||||||
|
@ -140,14 +146,17 @@ func (p *provider) DeleteWebhook(ctx context.Context, webhook *model.Webhook) er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d IN %s FILTER d.webhook_id == @webhook_id REMOVE { _key: d._key } IN %s", models.Collections.WebhookLog, models.Collections.WebhookLog)
|
query := fmt.Sprintf("FOR d IN %s FILTER d.webhook_id == @webhook_id REMOVE { _key: d._key } IN %s", models.Collections.WebhookLog, models.Collections.WebhookLog)
|
||||||
bindVars := map[string]interface{}{
|
bindVars := map[string]interface{}{
|
||||||
"webhook_id": webhook.ID,
|
"webhook_id": webhook.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/arangodb/go-driver"
|
||||||
arangoDriver "github.com/arangodb/go-driver"
|
arangoDriver "github.com/arangodb/go-driver"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -12,11 +13,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddWebhookLog to add webhook log
|
// AddWebhookLog to add webhook log
|
||||||
func (p *provider) AddWebhookLog(ctx context.Context, webhookLog *models.WebhookLog) (*model.WebhookLog, error) {
|
func (p *provider) AddWebhookLog(ctx context.Context, webhookLog models.WebhookLog) (*model.WebhookLog, error) {
|
||||||
if webhookLog.ID == "" {
|
if webhookLog.ID == "" {
|
||||||
webhookLog.ID = uuid.New().String()
|
webhookLog.ID = uuid.New().String()
|
||||||
webhookLog.Key = webhookLog.ID
|
webhookLog.Key = webhookLog.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
webhookLog.Key = webhookLog.ID
|
webhookLog.Key = webhookLog.ID
|
||||||
webhookLog.CreatedAt = time.Now().Unix()
|
webhookLog.CreatedAt = time.Now().Unix()
|
||||||
webhookLog.UpdatedAt = time.Now().Unix()
|
webhookLog.UpdatedAt = time.Now().Unix()
|
||||||
|
@ -29,38 +31,46 @@ func (p *provider) AddWebhookLog(ctx context.Context, webhookLog *models.Webhook
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListWebhookLogs to list webhook logs
|
// ListWebhookLogs to list webhook logs
|
||||||
func (p *provider) ListWebhookLogs(ctx context.Context, pagination *model.Pagination, webhookID string) (*model.WebhookLogs, error) {
|
func (p *provider) ListWebhookLogs(ctx context.Context, pagination model.Pagination, webhookID string) (*model.WebhookLogs, error) {
|
||||||
webhookLogs := []*model.WebhookLog{}
|
webhookLogs := []*model.WebhookLog{}
|
||||||
bindVariables := map[string]interface{}{}
|
bindVariables := map[string]interface{}{}
|
||||||
|
|
||||||
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit)
|
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit)
|
||||||
|
|
||||||
if webhookID != "" {
|
if webhookID != "" {
|
||||||
query = fmt.Sprintf("FOR d in %s FILTER d.webhook_id == @webhook_id SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit)
|
query = fmt.Sprintf("FOR d in %s FILTER d.webhook_id == @webhook_id SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.WebhookLog, pagination.Offset, pagination.Limit)
|
||||||
bindVariables = map[string]interface{}{
|
bindVariables = map[string]interface{}{
|
||||||
"webhook_id": webhookID,
|
"webhook_id": webhookID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sctx := arangoDriver.WithQueryFullCount(ctx)
|
|
||||||
|
sctx := driver.WithQueryFullCount(ctx)
|
||||||
cursor, err := p.db.Query(sctx, query, bindVariables)
|
cursor, err := p.db.Query(sctx, query, bindVariables)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close()
|
defer cursor.Close()
|
||||||
|
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
paginationClone.Total = cursor.Statistics().FullCount()
|
paginationClone.Total = cursor.Statistics().FullCount()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var webhookLog *models.WebhookLog
|
var webhookLog models.WebhookLog
|
||||||
meta, err := cursor.ReadDocument(ctx, &webhookLog)
|
meta, err := cursor.ReadDocument(ctx, &webhookLog)
|
||||||
|
|
||||||
if arangoDriver.IsNoMoreDocuments(err) {
|
if arangoDriver.IsNoMoreDocuments(err) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.Key != "" {
|
if meta.Key != "" {
|
||||||
webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog())
|
webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.WebhookLogs{
|
return &model.WebhookLogs{
|
||||||
Pagination: paginationClone,
|
Pagination: &paginationClone,
|
||||||
WebhookLogs: webhookLogs,
|
WebhookLogs: webhookLogs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
package cassandradb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gocql/gocql"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
|
||||||
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
|
||||||
if exists != nil {
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if authenticators.ID == "" {
|
|
||||||
authenticators.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticators.CreatedAt = time.Now().Unix()
|
|
||||||
authenticators.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(authenticators)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
|
||||||
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
|
||||||
decoder.UseNumber()
|
|
||||||
authenticatorsMap := map[string]interface{}{}
|
|
||||||
err = decoder.Decode(&authenticatorsMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := "("
|
|
||||||
values := "("
|
|
||||||
for key, value := range authenticatorsMap {
|
|
||||||
if value != nil {
|
|
||||||
if key == "_id" {
|
|
||||||
fields += "id,"
|
|
||||||
} else {
|
|
||||||
fields += key + ","
|
|
||||||
}
|
|
||||||
|
|
||||||
valueType := reflect.TypeOf(value)
|
|
||||||
if valueType.Name() == "string" {
|
|
||||||
values += fmt.Sprintf("'%s',", value.(string))
|
|
||||||
} else {
|
|
||||||
values += fmt.Sprintf("%v,", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = fields[:len(fields)-1] + ")"
|
|
||||||
values = values[:len(values)-1] + ")"
|
|
||||||
|
|
||||||
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.Authenticators, fields, values)
|
|
||||||
err = p.db.Query(query).Exec()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
|
||||||
authenticators.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(authenticators)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
|
||||||
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
|
||||||
decoder.UseNumber()
|
|
||||||
authenticatorsMap := map[string]interface{}{}
|
|
||||||
err = decoder.Decode(&authenticatorsMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFields := ""
|
|
||||||
for key, value := range authenticatorsMap {
|
|
||||||
if key == "_id" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if key == "_key" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if value == nil {
|
|
||||||
updateFields += fmt.Sprintf("%s = null, ", key)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
valueType := reflect.TypeOf(value)
|
|
||||||
if valueType.Name() == "string" {
|
|
||||||
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
|
|
||||||
} else {
|
|
||||||
updateFields += fmt.Sprintf("%s = %v, ", key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateFields = strings.Trim(updateFields, " ")
|
|
||||||
updateFields = strings.TrimSuffix(updateFields, ",")
|
|
||||||
|
|
||||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.Authenticators, updateFields, authenticators.ID)
|
|
||||||
err = p.db.Query(query).Exec()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticators, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
|
||||||
var authenticators models.Authenticator
|
|
||||||
query := fmt.Sprintf("SELECT id, user_id, method, secret, recovery_codes, verified_at, created_at, updated_at FROM %s WHERE user_id = '%s' AND method = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.Authenticators, userId, authenticatorType)
|
|
||||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&authenticators.ID, &authenticators.UserID, &authenticators.Method, &authenticators.Secret, &authenticators.RecoveryCodes, &authenticators.VerifiedAt, &authenticators.CreatedAt, &authenticators.UpdatedAt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &authenticators, nil
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user