diff --git a/.gitignore b/.gitignore index a0198d2..32cd7a0 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ dist .graphqlrc.yml codegen.yml backend/src/config/kc.config.json +backend/.env diff --git a/backend/.env b/backend/.env deleted file mode 100644 index f661589..0000000 --- a/backend/.env +++ /dev/null @@ -1,15 +0,0 @@ -ST_API_URL=http://server.home:3567 -ST_API_KEY=56e5638525c7385faaf4f10d781d64e81e0ae9e2ef5e6cf7328064e8f1d377c7 -APP_PORT=3300 -APP_NAME=Fluxem -APP_URL=http://localhost:3300 -WEBAPP_URL=http://localhost:3000 - -# This was inserted by `prisma init`: -# Environment variables declared in this file are automatically made available to Prisma. -# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema - -# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. -# See the documentation for all the connection string options: https://pris.ly/d/connection-strings - -DATABASE_URL="postgresql://postgres:502bde780ce8ba101572@server.home:5432/fluxem?schema=public" \ No newline at end of file diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index bb6f92e..8aaa818 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -1,6 +1,3 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - generator client { provider = "prisma-client-js" } @@ -10,11 +7,152 @@ datasource db { url = env("DATABASE_URL") } -model User { - id String @id @default(uuid()) - email String @unique - password String? - time_joined Int? - createdAt DateTime? @default(now()) - updatedAt DateTime? @updatedAt +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]) } diff --git a/backend/src/auth/supertokens/supertokens.service.ts b/backend/src/auth/supertokens/supertokens.service.ts index 270997d..491c742 100644 --- a/backend/src/auth/supertokens/supertokens.service.ts +++ b/backend/src/auth/supertokens/supertokens.service.ts @@ -6,6 +6,7 @@ import { 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'; @@ -19,7 +20,23 @@ export class SupertokensService { apiKey: config.apiKey, }, recipeList: [ - EmailPassword.init(), + 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({ diff --git a/backend/src/graphql/graphql.typings.ts b/backend/src/graphql/graphql.typings.ts index 2bf683d..69edfe6 100644 --- a/backend/src/graphql/graphql.typings.ts +++ b/backend/src/graphql/graphql.typings.ts @@ -13,6 +13,14 @@ export class CreateUserInput { password: string; } +export class UpdateUserInput { + email?: Nullable; + password?: Nullable; + time_joined?: Nullable; + createdAt?: Nullable; + updatedAt?: Nullable; +} + export class User { id: string; email: string; @@ -31,6 +39,8 @@ export abstract class IQuery { export abstract class IMutation { abstract createUser(createUserInput: CreateUserInput): User | Promise; + abstract updateUser(id: string, updateUserInput: UpdateUserInput): Nullable | Promise>; + abstract removeUser(id: string): Nullable | Promise>; } diff --git a/backend/src/users/users.graphql b/backend/src/users/users.graphql index 452248e..186dbe6 100644 --- a/backend/src/users/users.graphql +++ b/backend/src/users/users.graphql @@ -1,10 +1,10 @@ scalar DateTime type User { - id: String! + id: ID! email: String! password: String - time_joined: Int, + time_joined: Int createdAt: DateTime updatedAt: DateTime } @@ -14,17 +14,21 @@ input CreateUserInput { password: String! } -# input UpdateUserInput { -# id: String! -# } +input UpdateUserInput { + email: String + password: String + time_joined: Int + createdAt: DateTime + updatedAt: DateTime +} type Query { users: [User]! - user(id: String!): User + user(id: ID!): User } type Mutation { createUser(createUserInput: CreateUserInput!): User! - # updateUser(updateUserInput: UpdateUserInput!): User! - removeUser(id: String!): User + updateUser(id: ID!, updateUserInput: UpdateUserInput!): User + removeUser(id: ID!): User } diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 90f278c..5e8469d 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -18,17 +18,12 @@ export class UsersService { return this.prismaService.user.create({ data: createUserInput }); } - // findAll() { - // return `This action returns all users`; - // } - - // findOne(id: string) { - // return `This action returns a #${id} user`; - // } - - // update(id: string, updateUserInput: UpdateUserInput) { - // return `This action updates a #${id} user`; - // } + 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`;