Compare commits
1 Commits
auth-backe
...
solid-urql
| Author | SHA1 | Date | |
|---|---|---|---|
| fbe19ff41d |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -57,7 +57,7 @@ node_modules
|
|||||||
/build
|
/build
|
||||||
/.svelte-kit
|
/.svelte-kit
|
||||||
/package
|
/package
|
||||||
#.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
.vercel
|
.vercel
|
||||||
@@ -65,5 +65,3 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
.graphqlrc.yml
|
.graphqlrc.yml
|
||||||
codegen.yml
|
codegen.yml
|
||||||
backend/src/config/kc.config.json
|
|
||||||
backend/.env
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
project: 'tsconfig.json',
|
|
||||||
tsconfigRootDir : __dirname,
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
|
||||||
extends: [
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
],
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
ignorePatterns: ['.eslintrc.js'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/interface-name-prefix': 'off',
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
35
backend/.gitignore
vendored
35
backend/.gitignore
vendored
@@ -1,35 +0,0 @@
|
|||||||
# compiled output
|
|
||||||
/dist
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Tests
|
|
||||||
/coverage
|
|
||||||
/.nyc_output
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
/.idea
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.c9/
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# IDE - VSCode
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all"
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
|
||||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
|
||||||
|
|
||||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
|
||||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
|
||||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
|
||||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
|
||||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
|
||||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
|
||||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
|
||||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
|
||||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
|
||||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
|
||||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
|
||||||
</p>
|
|
||||||
<!--[](https://opencollective.com/nest#backer)
|
|
||||||
[](https://opencollective.com/nest#sponsor)-->
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running the app
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# development
|
|
||||||
$ npm run start
|
|
||||||
|
|
||||||
# watch mode
|
|
||||||
$ npm run start:dev
|
|
||||||
|
|
||||||
# production mode
|
|
||||||
$ npm run start:prod
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# unit tests
|
|
||||||
$ npm run test
|
|
||||||
|
|
||||||
# e2e tests
|
|
||||||
$ npm run test:e2e
|
|
||||||
|
|
||||||
# test coverage
|
|
||||||
$ npm run test:cov
|
|
||||||
```
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
|
||||||
|
|
||||||
## Stay in touch
|
|
||||||
|
|
||||||
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
|
||||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
|
||||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Nest is [MIT licensed](LICENSE).
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
|
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
const definitionFactory = new GraphQLDefinitionsFactory();
|
|
||||||
|
|
||||||
definitionFactory.generate({
|
|
||||||
typePaths: ['./**/*.graphql'],
|
|
||||||
path: join(process.cwd(), 'src/graphql/graphql.typings.ts'),
|
|
||||||
outputAs: 'class',
|
|
||||||
watch: true,
|
|
||||||
});
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* -------------------------------------------------------
|
|
||||||
* THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
|
||||||
* -------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
export class CreateUserInput {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class User {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
time_joined: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class IQuery {
|
|
||||||
abstract users(): Nullable<User>[] | Promise<Nullable<User>[]>;
|
|
||||||
|
|
||||||
abstract user(id: string): Nullable<User> | Promise<Nullable<User>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class IMutation {
|
|
||||||
abstract createUser(createUserInput: CreateUserInput): User | Promise<User>;
|
|
||||||
|
|
||||||
abstract removeUser(id: string): Nullable<User> | Promise<Nullable<User>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DateTime = any;
|
|
||||||
type Nullable<T> = T | null;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/nest-cli",
|
|
||||||
"collection": "@nestjs/schematics",
|
|
||||||
"sourceRoot": "src"
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "backend",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "",
|
|
||||||
"author": "",
|
|
||||||
"private": true,
|
|
||||||
"license": "UNLICENSED",
|
|
||||||
"scripts": {
|
|
||||||
"prebuild": "rimraf dist",
|
|
||||||
"build": "nest build",
|
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
||||||
"start": "nest start",
|
|
||||||
"start:dev": "nest start --watch",
|
|
||||||
"start:debug": "nest start --debug --watch",
|
|
||||||
"start:prod": "node dist/main",
|
|
||||||
"test": "jest",
|
|
||||||
"test:watch": "jest --watch",
|
|
||||||
"test:cov": "jest --coverage",
|
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
|
||||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
|
||||||
"gen:types": "ts-node ./graphql/generate.typings",
|
|
||||||
"prisma:gen": "prisma generate --watch",
|
|
||||||
"dev": "concurrently \"npm:start:dev\" \"npm:gen:types\" \"npm:prisma:gen\""
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@nestjs/apollo": "^10.1.0",
|
|
||||||
"@nestjs/common": "^9.0.0",
|
|
||||||
"@nestjs/config": "^2.2.0",
|
|
||||||
"@nestjs/core": "^9.0.0",
|
|
||||||
"@nestjs/graphql": "^10.1.1",
|
|
||||||
"@nestjs/mapped-types": "*",
|
|
||||||
"@nestjs/platform-express": "^9.0.0",
|
|
||||||
"@prisma/client": "4.3.1",
|
|
||||||
"apollo-server-express": "^3.10.2",
|
|
||||||
"graphql": "^16.6.0",
|
|
||||||
"reflect-metadata": "^0.1.13",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"rxjs": "^7.2.0",
|
|
||||||
"supertokens-node": "^11.3.0",
|
|
||||||
"ts-morph": "^16.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@nestjs/cli": "^9.0.0",
|
|
||||||
"@nestjs/schematics": "^9.0.0",
|
|
||||||
"@nestjs/testing": "^9.0.0",
|
|
||||||
"@types/express": "^4.17.13",
|
|
||||||
"@types/jest": "28.1.8",
|
|
||||||
"@types/node": "^16.0.0",
|
|
||||||
"@types/supertest": "^2.0.11",
|
|
||||||
"concurrently": "^7.4.0",
|
|
||||||
"jest": "28.1.3",
|
|
||||||
"prettier": "^2.3.2",
|
|
||||||
"prisma": "4.3.1",
|
|
||||||
"source-map-support": "^0.5.20",
|
|
||||||
"supertest": "^6.1.3",
|
|
||||||
"ts-jest": "28.0.8",
|
|
||||||
"ts-loader": "^9.2.3",
|
|
||||||
"ts-node": "^10.0.0",
|
|
||||||
"tsconfig-paths": "4.1.0",
|
|
||||||
"typescript": "^4.7.4"
|
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"rootDir": "src",
|
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"**/*.(t|j)s"
|
|
||||||
],
|
|
||||||
"coverageDirectory": "../coverage",
|
|
||||||
"testEnvironment": "node"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "User" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"email" TEXT NOT NULL,
|
|
||||||
"time_joined" INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
-- AlterTable
|
|
||||||
ALTER TABLE "User" ADD COLUMN "password" TEXT,
|
|
||||||
ALTER COLUMN "time_joined" DROP NOT NULL;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
-- AlterTable
|
|
||||||
ALTER TABLE "User" ADD COLUMN "createdAt" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
ADD COLUMN "updatedAt" TIMESTAMP(3);
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
/*
|
|
||||||
Warnings:
|
|
||||||
|
|
||||||
- You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost.
|
|
||||||
|
|
||||||
*/
|
|
||||||
-- DropTable
|
|
||||||
DROP TABLE "User";
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "all_auth_recipe_users" (
|
|
||||||
"user_id" CHAR(36) NOT NULL,
|
|
||||||
"recipe_id" VARCHAR(128) NOT NULL,
|
|
||||||
"time_joined" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "all_auth_recipe_users_pkey" PRIMARY KEY ("user_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "emailpassword_pswd_reset_tokens" (
|
|
||||||
"user_id" CHAR(36) NOT NULL,
|
|
||||||
"token" VARCHAR(128) NOT NULL,
|
|
||||||
"token_expiry" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "emailpassword_pswd_reset_tokens_pkey" PRIMARY KEY ("user_id","token")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "emailpassword_users" (
|
|
||||||
"user_id" CHAR(36) NOT NULL,
|
|
||||||
"email" VARCHAR(256) NOT NULL,
|
|
||||||
"password_hash" VARCHAR(128) NOT NULL,
|
|
||||||
"time_joined" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "emailpassword_users_pkey" PRIMARY KEY ("user_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "emailverification_tokens" (
|
|
||||||
"user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"email" VARCHAR(256) NOT NULL,
|
|
||||||
"token" VARCHAR(128) NOT NULL,
|
|
||||||
"token_expiry" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "emailverification_tokens_pkey" PRIMARY KEY ("user_id","email","token")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "emailverification_verified_emails" (
|
|
||||||
"user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"email" VARCHAR(256) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "emailverification_verified_emails_pkey" PRIMARY KEY ("user_id","email")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "jwt_signing_keys" (
|
|
||||||
"key_id" VARCHAR(255) NOT NULL,
|
|
||||||
"key_string" TEXT NOT NULL,
|
|
||||||
"algorithm" VARCHAR(10) NOT NULL,
|
|
||||||
"created_at" BIGINT,
|
|
||||||
|
|
||||||
CONSTRAINT "jwt_signing_keys_pkey" PRIMARY KEY ("key_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "key_value" (
|
|
||||||
"name" VARCHAR(128) NOT NULL,
|
|
||||||
"value" TEXT,
|
|
||||||
"created_at_time" BIGINT,
|
|
||||||
|
|
||||||
CONSTRAINT "key_value_pkey" PRIMARY KEY ("name")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "passwordless_codes" (
|
|
||||||
"code_id" CHAR(36) NOT NULL,
|
|
||||||
"device_id_hash" CHAR(44) NOT NULL,
|
|
||||||
"link_code_hash" CHAR(44) NOT NULL,
|
|
||||||
"created_at" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "passwordless_codes_pkey" PRIMARY KEY ("code_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "passwordless_devices" (
|
|
||||||
"device_id_hash" CHAR(44) NOT NULL,
|
|
||||||
"email" VARCHAR(256),
|
|
||||||
"phone_number" VARCHAR(256),
|
|
||||||
"link_code_salt" CHAR(44) NOT NULL,
|
|
||||||
"failed_attempts" INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "passwordless_devices_pkey" PRIMARY KEY ("device_id_hash")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "passwordless_users" (
|
|
||||||
"user_id" CHAR(36) NOT NULL,
|
|
||||||
"email" VARCHAR(256),
|
|
||||||
"phone_number" VARCHAR(256),
|
|
||||||
"time_joined" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "passwordless_users_pkey" PRIMARY KEY ("user_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "role_permissions" (
|
|
||||||
"role" VARCHAR(255) NOT NULL,
|
|
||||||
"permission" VARCHAR(255) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "role_permissions_pkey" PRIMARY KEY ("role","permission")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "roles" (
|
|
||||||
"role" VARCHAR(255) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "roles_pkey" PRIMARY KEY ("role")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "session_access_token_signing_keys" (
|
|
||||||
"created_at_time" BIGINT NOT NULL,
|
|
||||||
"value" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "session_access_token_signing_keys_pkey" PRIMARY KEY ("created_at_time")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "session_info" (
|
|
||||||
"session_handle" VARCHAR(255) NOT NULL,
|
|
||||||
"user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"refresh_token_hash_2" VARCHAR(128) NOT NULL,
|
|
||||||
"session_data" TEXT,
|
|
||||||
"expires_at" BIGINT NOT NULL,
|
|
||||||
"created_at_time" BIGINT NOT NULL,
|
|
||||||
"jwt_user_payload" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "session_info_pkey" PRIMARY KEY ("session_handle")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "thirdparty_users" (
|
|
||||||
"third_party_id" VARCHAR(28) NOT NULL,
|
|
||||||
"third_party_user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"user_id" CHAR(36) NOT NULL,
|
|
||||||
"email" VARCHAR(256) NOT NULL,
|
|
||||||
"time_joined" BIGINT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "thirdparty_users_pkey" PRIMARY KEY ("third_party_id","third_party_user_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "user_metadata" (
|
|
||||||
"user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"user_metadata" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "user_metadata_pkey" PRIMARY KEY ("user_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "user_roles" (
|
|
||||||
"user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"role" VARCHAR(255) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "user_roles_pkey" PRIMARY KEY ("user_id","role")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "userid_mapping" (
|
|
||||||
"supertokens_user_id" CHAR(36) NOT NULL,
|
|
||||||
"external_user_id" VARCHAR(128) NOT NULL,
|
|
||||||
"external_user_id_info" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "userid_mapping_pkey" PRIMARY KEY ("supertokens_user_id","external_user_id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "all_auth_recipe_users_pagination_index" ON "all_auth_recipe_users"("time_joined" DESC, "user_id" DESC);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "emailpassword_pswd_reset_tokens_token_key" ON "emailpassword_pswd_reset_tokens"("token");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "emailpassword_password_reset_token_expiry_index" ON "emailpassword_pswd_reset_tokens"("token_expiry");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "emailpassword_users_email_key" ON "emailpassword_users"("email");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "emailverification_tokens_token_key" ON "emailverification_tokens"("token");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "emailverification_tokens_index" ON "emailverification_tokens"("token_expiry");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "passwordless_codes_link_code_hash_key" ON "passwordless_codes"("link_code_hash");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "passwordless_codes_created_at_index" ON "passwordless_codes"("created_at");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "passwordless_codes_device_id_hash_index" ON "passwordless_codes"("device_id_hash");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "passwordless_devices_email_index" ON "passwordless_devices"("email");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "passwordless_devices_phone_number_index" ON "passwordless_devices"("phone_number");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "passwordless_users_email_key" ON "passwordless_users"("email");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "passwordless_users_phone_number_key" ON "passwordless_users"("phone_number");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "role_permissions_permission_index" ON "role_permissions"("permission");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "thirdparty_users_user_id_key" ON "thirdparty_users"("user_id");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "user_roles_role_index" ON "user_roles"("role");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "userid_mapping_supertokens_user_id_key" ON "userid_mapping"("supertokens_user_id");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "userid_mapping_external_user_id_key" ON "userid_mapping"("external_user_id");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "emailpassword_pswd_reset_tokens" ADD CONSTRAINT "emailpassword_pswd_reset_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "emailpassword_users"("user_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "passwordless_codes" ADD CONSTRAINT "passwordless_codes_device_id_hash_fkey" FOREIGN KEY ("device_id_hash") REFERENCES "passwordless_devices"("device_id_hash") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_role_fkey" FOREIGN KEY ("role") REFERENCES "roles"("role") ON DELETE CASCADE ON UPDATE NO ACTION;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_role_fkey" FOREIGN KEY ("role") REFERENCES "roles"("role") ON DELETE CASCADE ON UPDATE NO ACTION;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "userid_mapping" ADD CONSTRAINT "userid_mapping_supertokens_user_id_fkey" FOREIGN KEY ("supertokens_user_id") REFERENCES "all_auth_recipe_users"("user_id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Please do not edit this file manually
|
|
||||||
# It should be added in your version-control system (i.e. Git)
|
|
||||||
provider = "postgresql"
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PrismaService extends PrismaClient implements OnModuleInit {
|
|
||||||
async onModuleInit() {
|
|
||||||
await this.$connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
async enableShutdownHooks(app: INestApplication) {
|
|
||||||
this.$on('beforeExit', async () => {
|
|
||||||
await app.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
}
|
|
||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "postgresql"
|
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
model all_auth_recipe_users {
|
|
||||||
user_id String @id @db.Char(36)
|
|
||||||
recipe_id String @db.VarChar(128)
|
|
||||||
time_joined BigInt
|
|
||||||
userid_mapping userid_mapping?
|
|
||||||
|
|
||||||
@@index([time_joined(sort: Desc), user_id(sort: Desc)], map: "all_auth_recipe_users_pagination_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model emailpassword_pswd_reset_tokens {
|
|
||||||
user_id String @db.Char(36)
|
|
||||||
token String @unique @db.VarChar(128)
|
|
||||||
token_expiry BigInt
|
|
||||||
emailpassword_users emailpassword_users @relation(fields: [user_id], references: [user_id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@id([user_id, token])
|
|
||||||
@@index([token_expiry], map: "emailpassword_password_reset_token_expiry_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model emailpassword_users {
|
|
||||||
user_id String @id @db.Char(36)
|
|
||||||
email String @unique @db.VarChar(256)
|
|
||||||
password_hash String @db.VarChar(128)
|
|
||||||
time_joined BigInt
|
|
||||||
emailpassword_pswd_reset_tokens emailpassword_pswd_reset_tokens[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model emailverification_tokens {
|
|
||||||
user_id String @db.VarChar(128)
|
|
||||||
email String @db.VarChar(256)
|
|
||||||
token String @unique @db.VarChar(128)
|
|
||||||
token_expiry BigInt
|
|
||||||
|
|
||||||
@@id([user_id, email, token])
|
|
||||||
@@index([token_expiry], map: "emailverification_tokens_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model emailverification_verified_emails {
|
|
||||||
user_id String @db.VarChar(128)
|
|
||||||
email String @db.VarChar(256)
|
|
||||||
|
|
||||||
@@id([user_id, email])
|
|
||||||
}
|
|
||||||
|
|
||||||
model jwt_signing_keys {
|
|
||||||
key_id String @id @db.VarChar(255)
|
|
||||||
key_string String
|
|
||||||
algorithm String @db.VarChar(10)
|
|
||||||
created_at BigInt?
|
|
||||||
}
|
|
||||||
|
|
||||||
model key_value {
|
|
||||||
name String @id @db.VarChar(128)
|
|
||||||
value String?
|
|
||||||
created_at_time BigInt?
|
|
||||||
}
|
|
||||||
|
|
||||||
model passwordless_codes {
|
|
||||||
code_id String @id @db.Char(36)
|
|
||||||
device_id_hash String @db.Char(44)
|
|
||||||
link_code_hash String @unique @db.Char(44)
|
|
||||||
created_at BigInt
|
|
||||||
passwordless_devices passwordless_devices @relation(fields: [device_id_hash], references: [device_id_hash], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@index([created_at], map: "passwordless_codes_created_at_index")
|
|
||||||
@@index([device_id_hash], map: "passwordless_codes_device_id_hash_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model passwordless_devices {
|
|
||||||
device_id_hash String @id @db.Char(44)
|
|
||||||
email String? @db.VarChar(256)
|
|
||||||
phone_number String? @db.VarChar(256)
|
|
||||||
link_code_salt String @db.Char(44)
|
|
||||||
failed_attempts Int
|
|
||||||
passwordless_codes passwordless_codes[]
|
|
||||||
|
|
||||||
@@index([email], map: "passwordless_devices_email_index")
|
|
||||||
@@index([phone_number], map: "passwordless_devices_phone_number_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model passwordless_users {
|
|
||||||
user_id String @id @db.Char(36)
|
|
||||||
email String? @unique @db.VarChar(256)
|
|
||||||
phone_number String? @unique @db.VarChar(256)
|
|
||||||
time_joined BigInt
|
|
||||||
}
|
|
||||||
|
|
||||||
model role_permissions {
|
|
||||||
role String @db.VarChar(255)
|
|
||||||
permission String @db.VarChar(255)
|
|
||||||
roles roles @relation(fields: [role], references: [role], onDelete: Cascade, onUpdate: NoAction)
|
|
||||||
|
|
||||||
@@id([role, permission])
|
|
||||||
@@index([permission], map: "role_permissions_permission_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model roles {
|
|
||||||
role String @id @db.VarChar(255)
|
|
||||||
role_permissions role_permissions[]
|
|
||||||
user_roles user_roles[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model session_access_token_signing_keys {
|
|
||||||
created_at_time BigInt @id
|
|
||||||
value String?
|
|
||||||
}
|
|
||||||
|
|
||||||
model session_info {
|
|
||||||
session_handle String @id @db.VarChar(255)
|
|
||||||
user_id String @db.VarChar(128)
|
|
||||||
refresh_token_hash_2 String @db.VarChar(128)
|
|
||||||
session_data String?
|
|
||||||
expires_at BigInt
|
|
||||||
created_at_time BigInt
|
|
||||||
jwt_user_payload String?
|
|
||||||
}
|
|
||||||
|
|
||||||
model thirdparty_users {
|
|
||||||
third_party_id String @db.VarChar(28)
|
|
||||||
third_party_user_id String @db.VarChar(128)
|
|
||||||
user_id String @unique @db.Char(36)
|
|
||||||
email String @db.VarChar(256)
|
|
||||||
time_joined BigInt
|
|
||||||
|
|
||||||
@@id([third_party_id, third_party_user_id])
|
|
||||||
}
|
|
||||||
|
|
||||||
model user_metadata {
|
|
||||||
user_id String @id @db.VarChar(128)
|
|
||||||
user_metadata String
|
|
||||||
}
|
|
||||||
|
|
||||||
model user_roles {
|
|
||||||
user_id String @db.VarChar(128)
|
|
||||||
role String @db.VarChar(255)
|
|
||||||
roles roles @relation(fields: [role], references: [role], onDelete: Cascade, onUpdate: NoAction)
|
|
||||||
|
|
||||||
@@id([user_id, role])
|
|
||||||
@@index([role], map: "user_roles_role_index")
|
|
||||||
}
|
|
||||||
|
|
||||||
model userid_mapping {
|
|
||||||
supertokens_user_id String @unique @db.Char(36)
|
|
||||||
external_user_id String @unique @db.VarChar(128)
|
|
||||||
external_user_id_info String?
|
|
||||||
all_auth_recipe_users all_auth_recipe_users @relation(fields: [supertokens_user_id], references: [user_id], onDelete: Cascade, onUpdate: NoAction)
|
|
||||||
|
|
||||||
@@id([supertokens_user_id, external_user_id])
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Controller, Get, Session, UseGuards } from '@nestjs/common';
|
|
||||||
import { AuthGuard } from './auth/guards/auth.guard';
|
|
||||||
import { SessionContainer } from "supertokens-node/recipe/session";
|
|
||||||
|
|
||||||
@Controller()
|
|
||||||
export class AppController {
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
getHello(): string {
|
|
||||||
return "API";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get('test')
|
|
||||||
@UseGuards(AuthGuard)
|
|
||||||
async getTest(@Session() session: SessionContainer): Promise<string> {
|
|
||||||
// TODO: magic
|
|
||||||
return "magic";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { AppController } from './app.controller';
|
|
||||||
import { AuthModule } from './auth/auth.module';
|
|
||||||
import { ConfigModule } from '@nestjs/config';
|
|
||||||
import { GraphQLISODateTime, GraphQLModule } from '@nestjs/graphql';
|
|
||||||
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
|
||||||
import { ApolloServerPluginLandingPageLocalDefault } from 'apollo-server-core';
|
|
||||||
// import { UsersModule } from './users/users.module';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [
|
|
||||||
ConfigModule.forRoot(),
|
|
||||||
AuthModule.forRoot({
|
|
||||||
connectionURI: process.env.ST_API_URL,
|
|
||||||
apiKey: process.env.ST_API_KEY,
|
|
||||||
appInfo: {
|
|
||||||
appName: process.env.APP_NAME,
|
|
||||||
apiDomain: process.env.APP_URL,
|
|
||||||
websiteDomain: process.env.WEBAPP_URL,
|
|
||||||
apiBasePath: '/auth/api',
|
|
||||||
websiteBasePath: '/auth',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
||||||
driver: ApolloDriver,
|
|
||||||
typePaths: ['./**/*.graphql'],
|
|
||||||
debug: true,
|
|
||||||
playground: false,
|
|
||||||
resolvers: { DateTime: GraphQLISODateTime },
|
|
||||||
plugins: [ApolloServerPluginLandingPageLocalDefault()],
|
|
||||||
}),
|
|
||||||
// UsersModule,
|
|
||||||
],
|
|
||||||
controllers: [AppController],
|
|
||||||
providers: [],
|
|
||||||
})
|
|
||||||
export class AppModule {}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import {
|
|
||||||
DynamicModule,
|
|
||||||
MiddlewareConsumer,
|
|
||||||
Module,
|
|
||||||
NestModule,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { AuthMiddleware } from './middlewares/auth.middleware';
|
|
||||||
import { AuthModuleConfig, ConfigInjectionToken } from './interfaces/config.interface';
|
|
||||||
import { SupertokensService } from './supertokens/supertokens.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
providers: [],
|
|
||||||
exports: [],
|
|
||||||
controllers: [],
|
|
||||||
})
|
|
||||||
export class AuthModule implements NestModule {
|
|
||||||
configure(consumer: MiddlewareConsumer) {
|
|
||||||
consumer.apply(AuthMiddleware).forRoutes('*');
|
|
||||||
}
|
|
||||||
|
|
||||||
static forRoot({
|
|
||||||
connectionURI,
|
|
||||||
apiKey,
|
|
||||||
appInfo,
|
|
||||||
}: AuthModuleConfig): DynamicModule {
|
|
||||||
return {
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
useValue: {
|
|
||||||
appInfo,
|
|
||||||
connectionURI,
|
|
||||||
apiKey,
|
|
||||||
},
|
|
||||||
provide: ConfigInjectionToken,
|
|
||||||
},
|
|
||||||
SupertokensService,
|
|
||||||
],
|
|
||||||
exports: [],
|
|
||||||
imports: [],
|
|
||||||
module: AuthModule,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
||||||
|
|
||||||
export const Session = createParamDecorator(
|
|
||||||
(data: unknown, ctx: ExecutionContext) => {
|
|
||||||
const request = ctx.switchToHttp().getRequest();
|
|
||||||
return request.session;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
|
|
||||||
import { errorHandler } from 'supertokens-node/framework/express';
|
|
||||||
import { Error as STError } from 'supertokens-node';
|
|
||||||
import { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
|
|
||||||
|
|
||||||
|
|
||||||
@Catch(STError)
|
|
||||||
export class SupertokensExceptionFilter implements ExceptionFilter {
|
|
||||||
handler: ErrorRequestHandler;
|
|
||||||
constructor() {
|
|
||||||
this.handler = errorHandler();
|
|
||||||
}
|
|
||||||
catch(exception: Error, host: ArgumentsHost) {
|
|
||||||
const ctx = host.switchToHttp();
|
|
||||||
|
|
||||||
const resp = ctx.getResponse<Response>();
|
|
||||||
if (resp.headersSent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handler(
|
|
||||||
exception,
|
|
||||||
ctx.getRequest<Request>(),
|
|
||||||
resp,
|
|
||||||
ctx.getNext<NextFunction>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
|
||||||
import { Error as STError } from 'supertokens-node';
|
|
||||||
|
|
||||||
import { verifySession } from 'supertokens-node/recipe/session/framework/express';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthGuard implements CanActivate {
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const ctx = context.switchToHttp();
|
|
||||||
|
|
||||||
let err = undefined;
|
|
||||||
const resp = ctx.getResponse();
|
|
||||||
// You can create an optional version of this by passing {sessionRequired: false} to verifySession
|
|
||||||
await verifySession()(ctx.getRequest(), resp, (res) => {
|
|
||||||
err = res;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (resp.headersSent) {
|
|
||||||
throw new STError({
|
|
||||||
message: 'RESPONSE_SENT',
|
|
||||||
type: 'RESPONSE_SENT',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { AppInfo } from 'supertokens-node/types';
|
|
||||||
|
|
||||||
export const ConfigInjectionToken = 'ConfigInjectionToken';
|
|
||||||
|
|
||||||
export type AuthModuleConfig = {
|
|
||||||
appInfo: AppInfo;
|
|
||||||
connectionURI: string;
|
|
||||||
apiKey?: string;
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
|
||||||
import { middleware } from 'supertokens-node/framework/express';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthMiddleware implements NestMiddleware {
|
|
||||||
supertokensMiddleware: any;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.supertokensMiddleware = middleware();
|
|
||||||
}
|
|
||||||
|
|
||||||
use(req: Request, res: any, next: () => void) {
|
|
||||||
return this.supertokensMiddleware(req, res, next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import {
|
|
||||||
AuthModuleConfig,
|
|
||||||
ConfigInjectionToken,
|
|
||||||
} from '../interfaces/config.interface';
|
|
||||||
import supertokens from 'supertokens-node';
|
|
||||||
import ThirdPartyEmailPassword from 'supertokens-node/recipe/thirdpartyemailpassword';
|
|
||||||
import EmailPassword from 'supertokens-node/recipe/emailpassword';
|
|
||||||
import { STMPService } from 'supertokens-node/recipe/thirdpartyemailpassword/emaildelivery';
|
|
||||||
|
|
||||||
import Session from 'supertokens-node/recipe/session';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class SupertokensService {
|
|
||||||
constructor(@Inject(ConfigInjectionToken) private config: AuthModuleConfig) {
|
|
||||||
supertokens.init({
|
|
||||||
appInfo: config.appInfo,
|
|
||||||
supertokens: {
|
|
||||||
connectionURI: config.connectionURI,
|
|
||||||
apiKey: config.apiKey,
|
|
||||||
},
|
|
||||||
recipeList: [
|
|
||||||
EmailPassword.init({
|
|
||||||
emailDelivery: {
|
|
||||||
service: new STMPService({
|
|
||||||
smtpSettings: {
|
|
||||||
host: process.env.SMTP_HOST,
|
|
||||||
authUsername: process.env.SMTP_USERNAME,
|
|
||||||
password: process.env.SMTP_PASSWORD,
|
|
||||||
port: parseInt(process.env.SMTP_PORT),
|
|
||||||
from: {
|
|
||||||
name: process.env.SMTP_FROM_NAME,
|
|
||||||
email: process.env.SMTP_FROM_EMAIL,
|
|
||||||
},
|
|
||||||
secure: process.env.SMTP_SECURE ? true : false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
ThirdPartyEmailPassword.init({
|
|
||||||
providers: [
|
|
||||||
ThirdPartyEmailPassword.Google({
|
|
||||||
clientId:
|
|
||||||
'1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com',
|
|
||||||
clientSecret: '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',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Session.init({
|
|
||||||
// antiCsrf: "VIA_CUSTOM_HEADER",
|
|
||||||
jwt: {
|
|
||||||
enable: true,
|
|
||||||
issuer: `${process.env.APP_URL}/auth/api`,
|
|
||||||
},
|
|
||||||
override: {
|
|
||||||
functions: function (originalImplementation) {
|
|
||||||
return {
|
|
||||||
...originalImplementation,
|
|
||||||
createNewSession: async function (input) {
|
|
||||||
input.accessTokenPayload = {
|
|
||||||
...input.accessTokenPayload,
|
|
||||||
'https://hasura.io/jwt/claims': {
|
|
||||||
'x-hasura-user-id': input.userId,
|
|
||||||
'x-hasura-default-role': 'user',
|
|
||||||
'x-hasura-allowed-roles': ['user'],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return originalImplementation.createNewSession(input);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* -------------------------------------------------------
|
|
||||||
* THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
|
||||||
* -------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
export class CreateUserInput {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateUserInput {
|
|
||||||
email?: Nullable<string>;
|
|
||||||
password?: Nullable<string>;
|
|
||||||
time_joined?: Nullable<number>;
|
|
||||||
createdAt?: Nullable<DateTime>;
|
|
||||||
updatedAt?: Nullable<DateTime>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class User {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
password?: Nullable<string>;
|
|
||||||
time_joined?: Nullable<number>;
|
|
||||||
createdAt?: Nullable<DateTime>;
|
|
||||||
updatedAt?: Nullable<DateTime>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class IQuery {
|
|
||||||
abstract users(): Nullable<User>[] | Promise<Nullable<User>[]>;
|
|
||||||
|
|
||||||
abstract user(id: string): Nullable<User> | Promise<Nullable<User>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class IMutation {
|
|
||||||
abstract createUser(createUserInput: CreateUserInput): User | Promise<User>;
|
|
||||||
|
|
||||||
abstract updateUser(id: string, updateUserInput: UpdateUserInput): Nullable<User> | Promise<Nullable<User>>;
|
|
||||||
|
|
||||||
abstract removeUser(id: string): Nullable<User> | Promise<Nullable<User>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DateTime = any;
|
|
||||||
type Nullable<T> = T | null;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { NestFactory } from '@nestjs/core';
|
|
||||||
import { AppModule } from './app.module';
|
|
||||||
import supertokens from 'supertokens-node';
|
|
||||||
import { SupertokensExceptionFilter } from './auth/filters/auth.filter';
|
|
||||||
// import { PrismaService } from 'prisma/prisma.service';
|
|
||||||
|
|
||||||
async function bootstrap() {
|
|
||||||
const app = await NestFactory.create(AppModule);
|
|
||||||
app.enableCors({
|
|
||||||
origin: [process.env.WEBAPP_URL, 'http://server.home:8081'],
|
|
||||||
allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()],
|
|
||||||
credentials: true,
|
|
||||||
});
|
|
||||||
app.useGlobalFilters(new SupertokensExceptionFilter());
|
|
||||||
// const prismaService = app.get(PrismaService);
|
|
||||||
// await prismaService.enableShutdownHooks(app);
|
|
||||||
|
|
||||||
await app.listen(process.env.APP_PORT);
|
|
||||||
}
|
|
||||||
bootstrap();
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
scalar DateTime
|
|
||||||
|
|
||||||
type User {
|
|
||||||
id: ID!
|
|
||||||
email: String!
|
|
||||||
password: String
|
|
||||||
time_joined: Int
|
|
||||||
createdAt: DateTime
|
|
||||||
updatedAt: DateTime
|
|
||||||
}
|
|
||||||
|
|
||||||
input CreateUserInput {
|
|
||||||
email: String!
|
|
||||||
password: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input UpdateUserInput {
|
|
||||||
email: String
|
|
||||||
password: String
|
|
||||||
time_joined: Int
|
|
||||||
createdAt: DateTime
|
|
||||||
updatedAt: DateTime
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
users: [User]!
|
|
||||||
user(id: ID!): User
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
createUser(createUserInput: CreateUserInput!): User!
|
|
||||||
updateUser(id: ID!, updateUserInput: UpdateUserInput!): User
|
|
||||||
removeUser(id: ID!): User
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { UsersService } from './users.service';
|
|
||||||
import { UsersResolver } from './users.resolver';
|
|
||||||
import { PrismaService } from 'prisma/prisma.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
providers: [PrismaService, UsersResolver, UsersService],
|
|
||||||
})
|
|
||||||
export class UsersModule {}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
|
|
||||||
import { Prisma } from '@prisma/client';
|
|
||||||
import { UsersService } from './users.service';
|
|
||||||
|
|
||||||
@Resolver('User')
|
|
||||||
export class UsersResolver {
|
|
||||||
constructor(private readonly usersService: UsersService) {}
|
|
||||||
|
|
||||||
// @Mutation('createUser')
|
|
||||||
// create(@Args('createUserInput') createUserInput: Prisma.UserCreateInput) {
|
|
||||||
// return this.usersService.create(createUserInput);
|
|
||||||
// }
|
|
||||||
// @Query('users')
|
|
||||||
// findAll(@Args('params') params?: Prisma.UserFindManyArgs) {
|
|
||||||
// return this.usersService.users(params);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Query('user')
|
|
||||||
// findOne(@Args('id') id: string) {
|
|
||||||
// return this.usersService.user({ id });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Mutation('updateUser')
|
|
||||||
// update(@Args('updateUserInput') updateUserInput: UpdateUserInput) {
|
|
||||||
// return this.usersService.update(updateUserInput.id, updateUserInput);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Mutation('removeUser')
|
|
||||||
// remove(@Args('id') id: string) {
|
|
||||||
// return this.usersService.remove(id);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
// import { Prisma, User } from '@prisma/client';
|
|
||||||
import { PrismaService } from 'prisma/prisma.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UsersService {
|
|
||||||
constructor(private readonly prismaService: PrismaService) {}
|
|
||||||
|
|
||||||
// async user(uniqueInput: Prisma.UserWhereUniqueInput) {
|
|
||||||
// return await this.prismaService.user.findUnique({ where: uniqueInput });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async users(params?: Prisma.UserFindManyArgs) {
|
|
||||||
// return await this.prismaService.user.findMany(params);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// create(createUserInput: Prisma.UserCreateInput) {
|
|
||||||
// return this.prismaService.user.create({ data: createUserInput });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// update(id: string, userUpdateInput: Prisma.UserUpdateInput) {
|
|
||||||
// return this.prismaService.user.update({
|
|
||||||
// where: { id },
|
|
||||||
// data: userUpdateInput,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// remove(id: string) {
|
|
||||||
// return `This action removes a #${id} user`;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { INestApplication } from '@nestjs/common';
|
|
||||||
import * as request from 'supertest';
|
|
||||||
import { AppModule } from './../src/app.module';
|
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
|
||||||
let app: INestApplication;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [AppModule],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
app = moduleFixture.createNestApplication();
|
|
||||||
await app.init();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('/ (GET)', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get('/')
|
|
||||||
.expect(200)
|
|
||||||
.expect('Hello World!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"moduleFileExtensions": ["js", "json", "ts"],
|
|
||||||
"rootDir": ".",
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"testRegex": ".e2e-spec.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"declaration": true,
|
|
||||||
"removeComments": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"target": "es2017",
|
|
||||||
"sourceMap": true,
|
|
||||||
"outDir": "./dist",
|
|
||||||
"baseUrl": "./",
|
|
||||||
"incremental": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strictNullChecks": false,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"strictBindCallApply": false,
|
|
||||||
"forceConsistentCasingInFileNames": false,
|
|
||||||
"noFallthroughCasesInSwitch": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5261
backend/yarn.lock
5261
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
|||||||
import type { Component } from "solid-js";
|
|
||||||
import AppRouter from "./routes";
|
|
||||||
|
|
||||||
import SuperTokens from "supertokens-web-js";
|
|
||||||
import EmailPass from "supertokens-web-js/recipe/emailpassword";
|
|
||||||
import Session from "supertokens-web-js/recipe/session";
|
|
||||||
import AuthProvider from "./context/AuthContext";
|
|
||||||
|
|
||||||
SuperTokens.init({
|
|
||||||
appInfo: {
|
|
||||||
apiDomain: "http://localhost:3300",
|
|
||||||
apiBasePath: "/auth/api",
|
|
||||||
appName: "Fluxem",
|
|
||||||
},
|
|
||||||
recipeList: [EmailPass.init(), Session.init()],
|
|
||||||
});
|
|
||||||
|
|
||||||
const App: Component = () => {
|
|
||||||
return (
|
|
||||||
<AuthProvider>
|
|
||||||
<AppRouter />
|
|
||||||
</AuthProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
const AuthLoader = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>Auth Loading...</h2>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AuthLoader;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
interface TestComponentProps {
|
|
||||||
// add props here
|
|
||||||
}
|
|
||||||
|
|
||||||
function TestComponent(props: TestComponentProps) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>TestComponent</h2>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TestComponent;
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import { useNavigate } from "@solidjs/router";
|
|
||||||
import { createContext, onMount, Show, useContext } from "solid-js";
|
|
||||||
import { createStore } from "solid-js/store";
|
|
||||||
import { UserType } from "supertokens-web-js/recipe/emailpassword";
|
|
||||||
import AuthLoader from "../components/AuthLoader";
|
|
||||||
import { currentUser } from "../services/auth.service";
|
|
||||||
|
|
||||||
const AuthStateContext = createContext();
|
|
||||||
const AuthDispatchContext = createContext<any>();
|
|
||||||
|
|
||||||
interface InitState {
|
|
||||||
isLoading: boolean;
|
|
||||||
isAuthenticated: boolean;
|
|
||||||
currentUser: UserType | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialState: InitState = {
|
|
||||||
isLoading: true,
|
|
||||||
isAuthenticated: false,
|
|
||||||
currentUser: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const AuthProvider = (props: any) => {
|
|
||||||
const [store, setStore] = createStore(initialState);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const loadCurrentUser = async () => {
|
|
||||||
const user = await currentUser();
|
|
||||||
if (user) {
|
|
||||||
setStore("isAuthenticated", true);
|
|
||||||
setStore("currentUser", user);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
await loadCurrentUser();
|
|
||||||
setStore("isLoading", false);
|
|
||||||
});
|
|
||||||
|
|
||||||
const setCurrentUser = (user?: UserType) => {
|
|
||||||
if (user) {
|
|
||||||
setStore("isAuthenticated", true);
|
|
||||||
setStore("currentUser", user);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const removeCurrentUser = () => {
|
|
||||||
setStore("isAuthenticated", false);
|
|
||||||
setStore("currentUser", null);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthStateContext.Provider value={store}>
|
|
||||||
<AuthDispatchContext.Provider
|
|
||||||
value={{
|
|
||||||
setCurrentUser,
|
|
||||||
removeCurrentUser,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Show when={!store.isLoading} fallback={<AuthLoader />}>
|
|
||||||
{props.children}
|
|
||||||
</Show>
|
|
||||||
</AuthDispatchContext.Provider>
|
|
||||||
</AuthStateContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AuthProvider;
|
|
||||||
export const useAuthState = () => useContext(AuthStateContext);
|
|
||||||
export const useAuthDispatch = () => useContext(AuthDispatchContext);
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { createClient } from "@urql/core";
|
|
||||||
|
|
||||||
const client = createClient({
|
|
||||||
url: ''
|
|
||||||
});
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import { useNavigate } from "@solidjs/router";
|
|
||||||
import { createSignal } from "solid-js";
|
|
||||||
import { createStore } from "solid-js/store";
|
|
||||||
|
|
||||||
import { useAuthDispatch } from "../../context/AuthContext";
|
|
||||||
import {
|
|
||||||
delUserFromLocalStorage,
|
|
||||||
loginService,
|
|
||||||
logoutService,
|
|
||||||
setUserInLocalStorage,
|
|
||||||
} from "../../services/auth.service";
|
|
||||||
|
|
||||||
const useLogin = () => {
|
|
||||||
const [loading, setLoading] = createSignal(false);
|
|
||||||
const [form, setForm] = createStore({
|
|
||||||
email: "",
|
|
||||||
password: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const { setCurrentUser } = useAuthDispatch();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const handleInput = (ev: any) => {
|
|
||||||
setForm([ev.currentTarget.name], ev.currentTarget.value);
|
|
||||||
};
|
|
||||||
const handleLogin = async (ev: any) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const loginData = await loginService({
|
|
||||||
formFields: [
|
|
||||||
{ id: "email", value: form.email },
|
|
||||||
{ id: "password", value: form.password },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (loginData.status === "OK") {
|
|
||||||
setCurrentUser(loginData.user);
|
|
||||||
setUserInLocalStorage(loginData.user);
|
|
||||||
navigate("/", { replace: true });
|
|
||||||
}
|
|
||||||
if (loginData.status === "FIELD_ERROR") {
|
|
||||||
// TODO: Handle error in UI
|
|
||||||
console.log("FIELD_ERROR", loginData);
|
|
||||||
}
|
|
||||||
if (loginData.status === "WRONG_CREDENTIALS_ERROR") {
|
|
||||||
// TODO: Handle error in UI
|
|
||||||
console.log("WRONG_CREDENTIALS_ERROR", loginData);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
//TODO: Handle error in UI
|
|
||||||
console.error("ERRRRRRRRRRr", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { handleInput, loading, handleLogin, form };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useLogin;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { createSignal } from "solid-js";
|
|
||||||
import { useAuthDispatch } from "../../context/AuthContext";
|
|
||||||
import {
|
|
||||||
delUserFromLocalStorage,
|
|
||||||
logoutService,
|
|
||||||
} from "../../services/auth.service";
|
|
||||||
|
|
||||||
const useLogout = () => {
|
|
||||||
const [loading, setLoading] = createSignal(false);
|
|
||||||
const { removeCurrentUser } = useAuthDispatch();
|
|
||||||
|
|
||||||
const handleLogout = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
await logoutService();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
removeCurrentUser();
|
|
||||||
delUserFromLocalStorage();
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { handleLogout, loading };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useLogout;
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { createSignal } from "solid-js";
|
|
||||||
import { createStore } from "solid-js/store";
|
|
||||||
import EmailPassRecipe from "supertokens-web-js/recipe/emailpassword";
|
|
||||||
|
|
||||||
const useRegister = () => {
|
|
||||||
const [loading, setLoading] = createSignal(false);
|
|
||||||
const [form, setForm] = createStore({
|
|
||||||
email: "",
|
|
||||||
password: "",
|
|
||||||
});
|
|
||||||
const handleInput = (ev: any) => {
|
|
||||||
setForm([ev.currentTarget.name], ev.currentTarget.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRegister = async (ev: any) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const login = await EmailPassRecipe.signUp({
|
|
||||||
formFields: [
|
|
||||||
{ id: "email", value: form.email },
|
|
||||||
{ id: "password", value: form.password },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return { handleInput, loading, handleRegister, form };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useRegister;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { useNavigate } from "@solidjs/router";
|
|
||||||
import { Component, Show } from "solid-js";
|
|
||||||
import { useAuthState } from "../../../context/AuthContext";
|
|
||||||
import useLogin from "../../../hooks/auth/login.hook";
|
|
||||||
const Login: Component = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { handleLogin, handleInput, loading, form } = useLogin();
|
|
||||||
const authState: any = useAuthState();
|
|
||||||
if (authState.isAuthenticated) {
|
|
||||||
navigate("/");
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<form onsubmit={handleLogin}>
|
|
||||||
<div>
|
|
||||||
<label for="email">Username or Email</label>
|
|
||||||
<br />
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
placeholder="Email/Username"
|
|
||||||
value={form.email}
|
|
||||||
onInput={handleInput}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="password">Password</label>
|
|
||||||
<br />
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
placeholder="Password"
|
|
||||||
value={form.password}
|
|
||||||
onInput={handleInput}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<button disabled={loading()} type="submit">
|
|
||||||
<Show when={!loading()} fallback="Logging in...">
|
|
||||||
Log In
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export default Login;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import EmailPassRecipe from "supertokens-web-js/recipe/emailpassword";
|
|
||||||
|
|
||||||
const VerifyEmail = () => {
|
|
||||||
let text = "Verifying email";
|
|
||||||
const verifyEmail = EmailPassRecipe.verifyEmail();
|
|
||||||
verifyEmail.then((data) => {
|
|
||||||
console.log(data);
|
|
||||||
text = "Email verified successfully";
|
|
||||||
});
|
|
||||||
// console.log(verifyEmail);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>{text}</h2>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VerifyEmail;
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { Component, Show } from "solid-js";
|
|
||||||
import { useAuthState } from "../../../context/AuthContext";
|
|
||||||
import useLogin from "../../../hooks/auth/login.hook";
|
|
||||||
import useLogout from "../../../hooks/auth/logout.hook";
|
|
||||||
|
|
||||||
const Home: Component = () => {
|
|
||||||
const authState: any = useAuthState();
|
|
||||||
const { handleLogout, loading } = useLogout();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>Home</p>
|
|
||||||
|
|
||||||
<Show when={authState?.isAuthenticated}>
|
|
||||||
<div>
|
|
||||||
<p>Authenticated</p>
|
|
||||||
<button onclick={handleLogout} disabled={loading()}>
|
|
||||||
<Show when={!loading()} fallback="Signing out...">
|
|
||||||
Sign Out
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
<p>{JSON.stringify(authState)}</p>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
<Show when={!authState?.isAuthenticated}>
|
|
||||||
<a href="/auth/login">Login</a>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Home;
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import EmailPassRecipe, {
|
|
||||||
UserType,
|
|
||||||
} from "supertokens-web-js/recipe/emailpassword";
|
|
||||||
import Session from "supertokens-web-js/recipe/session";
|
|
||||||
|
|
||||||
const curentUserKey = "current_user";
|
|
||||||
|
|
||||||
export interface AuthData {
|
|
||||||
formFields: FormFields[];
|
|
||||||
}
|
|
||||||
interface FormFields {
|
|
||||||
id: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentUser = async () => {
|
|
||||||
// This method might produce bugs. Localstorage may not be the same as logged in user
|
|
||||||
// TODO: endpoing in backend (getCurrentUser)
|
|
||||||
if (
|
|
||||||
localStorage.getItem("current_user") &&
|
|
||||||
(await Session.doesSessionExist())
|
|
||||||
) {
|
|
||||||
let sessionUserId = await Session.getUserId();
|
|
||||||
const user = getUserFromLocaStorage();
|
|
||||||
if (user) {
|
|
||||||
if (sessionUserId === user.id) {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginService = async (loginData: AuthData) => {
|
|
||||||
return await EmailPassRecipe.signIn(loginData);
|
|
||||||
};
|
|
||||||
|
|
||||||
const logoutService = async () => {
|
|
||||||
await EmailPassRecipe.signOut();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setUserInLocalStorage = (user: UserType) => {
|
|
||||||
localStorage.setItem(curentUserKey, JSON.stringify(user));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getUserFromLocaStorage = () => {
|
|
||||||
const user: UserType = JSON.parse(localStorage.getItem(curentUserKey)!);
|
|
||||||
return user;
|
|
||||||
};
|
|
||||||
|
|
||||||
const delUserFromLocalStorage = () => {
|
|
||||||
localStorage.removeItem(curentUserKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
export {
|
|
||||||
loginService,
|
|
||||||
logoutService,
|
|
||||||
currentUser,
|
|
||||||
setUserInLocalStorage,
|
|
||||||
delUserFromLocalStorage,
|
|
||||||
};
|
|
||||||
@@ -28,6 +28,6 @@
|
|||||||
"@urql/core": "^3.0.3",
|
"@urql/core": "^3.0.3",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"solid-js": "^1.5.1",
|
"solid-js": "^1.5.1",
|
||||||
"supertokens-web-js": "^0.1.6"
|
"solid-urql": "^0.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14
src/App.tsx
Normal file
14
src/App.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { Component } from "solid-js";
|
||||||
|
import { Provider } from "solid-urql";
|
||||||
|
import client from "./graphql/client";
|
||||||
|
import AppRouter from "./routes";
|
||||||
|
|
||||||
|
const App: Component = () => {
|
||||||
|
return (
|
||||||
|
<Provider value={client}>
|
||||||
|
<AppRouter />
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
7
src/graphql/client.ts
Normal file
7
src/graphql/client.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createClient } from "solid-urql";
|
||||||
|
|
||||||
|
const client = createClient({
|
||||||
|
url: 'https://hasura.apps.artservis.al/v1/graphql'
|
||||||
|
});
|
||||||
|
|
||||||
|
export default client;
|
||||||
22
src/hooks/auth/login.hook.ts
Normal file
22
src/hooks/auth/login.hook.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { createSignal } from "solid-js";
|
||||||
|
import { createStore } from "solid-js/store";
|
||||||
|
|
||||||
|
const useLogin = () => {
|
||||||
|
const [loading, setLoading] = createSignal(false);
|
||||||
|
const [form, setForm] = createStore({
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
const handleInput = (ev: any) => {
|
||||||
|
setForm([ev.currentTarget.name], ev.currentTarget.value);
|
||||||
|
};
|
||||||
|
const handleLogin = (ev: any) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handleInput, loading, handleLogin, form };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLogin;
|
||||||
@@ -4,6 +4,7 @@ import { render } from "solid-js/web";
|
|||||||
import "./index.css";
|
import "./index.css";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { Router } from "@solidjs/router";
|
import { Router } from "@solidjs/router";
|
||||||
|
|
||||||
render(
|
render(
|
||||||
() => (
|
() => (
|
||||||
<Router>
|
<Router>
|
||||||
@@ -5,7 +5,7 @@ const Home = lazy(() => import("./views/home/Home"));
|
|||||||
const AuthLayout = lazy(() => import("./views/auth/AuthLayout"));
|
const AuthLayout = lazy(() => import("./views/auth/AuthLayout"));
|
||||||
const Login = lazy(() => import("./views/auth/Login"));
|
const Login = lazy(() => import("./views/auth/Login"));
|
||||||
const Register = lazy(() => import("./views/auth/Register"));
|
const Register = lazy(() => import("./views/auth/Register"));
|
||||||
const VerifyEmail = lazy(() => import("./views/auth/VerifyEmail"));
|
|
||||||
const AppRouter: Component = () => {
|
const AppRouter: Component = () => {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
@@ -13,7 +13,6 @@ const AppRouter: Component = () => {
|
|||||||
<Route path="/auth" element={<AuthLayout />}>
|
<Route path="/auth" element={<AuthLayout />}>
|
||||||
<Route path="/login" element={<Login />}></Route>
|
<Route path="/login" element={<Login />}></Route>
|
||||||
<Route path="/register" element={<Register />}></Route>
|
<Route path="/register" element={<Register />}></Route>
|
||||||
<Route path="/verify-email" element={<VerifyEmail />}></Route>
|
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
39
src/routes/views/auth/Login.tsx
Normal file
39
src/routes/views/auth/Login.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Component, Show } from "solid-js";
|
||||||
|
import useLogin from "../../../hooks/auth/login.hook";
|
||||||
|
const { handleLogin, handleInput, loading, form } = useLogin();
|
||||||
|
const Login: Component = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<form onsubmit={handleLogin}>
|
||||||
|
<div>
|
||||||
|
{/* <label for="name">Username or Email</label> */}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
placeholder="Email/Username"
|
||||||
|
value={form.username}
|
||||||
|
onInput={handleInput}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{/* <label for="name">Password</label> */}
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={form.password}
|
||||||
|
onInput={handleInput}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button disabled={loading()} type="submit">
|
||||||
|
<Show when={!loading()} fallback="Logging in...">
|
||||||
|
Log In
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
||||||
20
src/routes/views/auth/Register.tsx
Normal file
20
src/routes/views/auth/Register.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { Component } from "solid-js";
|
||||||
|
import { useAllUsersQuery } from "../../../graphql/generated/graphql";
|
||||||
|
|
||||||
|
const mockHasura = () => {
|
||||||
|
const getUsers = useAllUsersQuery();
|
||||||
|
console.log(getUsers);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Register: Component = () => {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Register</h2>
|
||||||
|
<button onclick={mockHasura}>Mock</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Register;
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { Component } from "solid-js";
|
import type { Component } from "solid-js";
|
||||||
const Register: Component = () => {
|
|
||||||
|
const Home: Component = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Register</h2>
|
<h2>Home</h2>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Register;
|
export default Home;
|
||||||
@@ -1297,13 +1297,6 @@ braces@^3.0.2, braces@~3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
browser-tabs-lock@^1.2.14:
|
|
||||||
version "1.2.15"
|
|
||||||
resolved "https://registry.yarnpkg.com/browser-tabs-lock/-/browser-tabs-lock-1.2.15.tgz#d5012e652e2a0cb4eba471b0a2300c2fa5d92788"
|
|
||||||
integrity sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==
|
|
||||||
dependencies:
|
|
||||||
lodash ">=4.17.21"
|
|
||||||
|
|
||||||
browserslist@^4.20.2:
|
browserslist@^4.20.2:
|
||||||
version "4.21.3"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
|
||||||
@@ -2472,7 +2465,7 @@ lodash.once@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
|
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
|
||||||
|
|
||||||
lodash@>=4.17.21, lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0:
|
lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
@@ -3091,6 +3084,11 @@ solid-refresh@^0.4.1:
|
|||||||
"@babel/helper-module-imports" "^7.16.7"
|
"@babel/helper-module-imports" "^7.16.7"
|
||||||
"@babel/types" "^7.18.4"
|
"@babel/types" "^7.18.4"
|
||||||
|
|
||||||
|
solid-urql@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/solid-urql/-/solid-urql-0.2.0.tgz#b6305480a4e46d176a195a4bc586291e19944a7a"
|
||||||
|
integrity sha512-3sLtHBbbfwANBTP0toZLdVeRKa4UJDBjgfP5rv2z4ip4+a8vBlfFHWy/5N6fgxBhQ0IdbuYKRAcJECkJd80auw==
|
||||||
|
|
||||||
source-map-js@^1.0.2:
|
source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
@@ -3136,27 +3134,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^5.0.1"
|
ansi-regex "^5.0.1"
|
||||||
|
|
||||||
supertokens-js-override@0.0.4, supertokens-js-override@^0.0.4:
|
|
||||||
version "0.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/supertokens-js-override/-/supertokens-js-override-0.0.4.tgz#9af583fbc5e1f0195dbb358c4fcf75f44c76dc09"
|
|
||||||
integrity sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==
|
|
||||||
|
|
||||||
supertokens-web-js@^0.1.6:
|
|
||||||
version "0.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/supertokens-web-js/-/supertokens-web-js-0.1.6.tgz#81165f9f7604518db05088a8a527c756b3b48178"
|
|
||||||
integrity sha512-Cyu97r6tRJc4ryiKhOqYlqYQ+1XB5x5rJfMudg0kgANu7bvNMo39mI4KAb4eLaXQfXAPQ6Y/As+uqUVP7gbKDw==
|
|
||||||
dependencies:
|
|
||||||
supertokens-js-override "0.0.4"
|
|
||||||
supertokens-website "^13.0.2"
|
|
||||||
|
|
||||||
supertokens-website@^13.0.2:
|
|
||||||
version "13.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/supertokens-website/-/supertokens-website-13.0.2.tgz#c1eba0d1745ee2d7c019fa88eb9d7029309fc6e9"
|
|
||||||
integrity sha512-WVCHky05ndJhPXW2khAWy3CBlWn1ZwRF32TPTiGgYLrpQYO9q5doHJi9z2H1OiSmOEFJDEKWtaPW9HxAHABo1Q==
|
|
||||||
dependencies:
|
|
||||||
browser-tabs-lock "^1.2.14"
|
|
||||||
supertokens-js-override "^0.0.4"
|
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
Reference in New Issue
Block a user