Code base
- Authentication support - Env file example for easy and secure setup - Dynamic GraphQL Module loading - Database linking using Prisma
This commit is contained in:
parent
e39484b960
commit
4e12b56e2a
10
.env.example
Normal file
10
.env.example
Normal file
@ -0,0 +1,10 @@
|
||||
JWT_SECRET=
|
||||
ENABLE_REGISTRATION=
|
||||
IS_ATTACHED=true
|
||||
INSTALLED_MODULES=
|
||||
|
||||
## Enable module list
|
||||
|
||||
# Almost mandatory, not including this module could break everything
|
||||
AUTHENTICATION=
|
||||
TEMENOS=
|
5
datamodel.prisma
Normal file
5
datamodel.prisma
Normal file
@ -0,0 +1,5 @@
|
||||
type User {
|
||||
id: ID! @unique
|
||||
username: String! @unique
|
||||
password: String!
|
||||
}
|
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal file
@ -0,0 +1,30 @@
|
||||
version: '3'
|
||||
services:
|
||||
prisma:
|
||||
image: prismagraphql/prisma:1.23
|
||||
restart: always
|
||||
ports:
|
||||
- "4466:4466"
|
||||
environment:
|
||||
PRISMA_CONFIG: |
|
||||
port: 4466
|
||||
# uncomment the next line and provide the env var PRISMA_MANAGEMENT_API_SECRET=my-secret to activate cluster security
|
||||
# managementApiSecret: my-secret
|
||||
databases:
|
||||
default:
|
||||
connector: mysql
|
||||
host: mysql
|
||||
port: 3306
|
||||
user: root
|
||||
password: prisma
|
||||
migrations: true
|
||||
rawAccess: true
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: prisma
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
volumes:
|
||||
mysql:
|
6589
package-lock.json
generated
Normal file
6589
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
package.json
Normal file
36
package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "amaxa",
|
||||
"version": "0.0.1",
|
||||
"description": "Amaxa is the API mainframe used to centralize every GraphQL Module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon src/index.js",
|
||||
"start": "node src/index.js",
|
||||
"deploy": "prisma deploy"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/The-School-of-Athens/Amaxa.git"
|
||||
},
|
||||
"author": "Tanguy Herbron",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/The-School-of-Athens/Amaxa/issues"
|
||||
},
|
||||
"homepage": "https://github.com/The-School-of-Athens/Amaxa#readme",
|
||||
"dependencies": {
|
||||
"@graphql-modules/core": "^0.7.15",
|
||||
"apacheconf": "0.0.5",
|
||||
"apollo-server": "^2.2.6",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"graphql": "^14.0.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"prisma": "^1.23.0",
|
||||
"prisma-client-lib": "^1.21.1",
|
||||
"temenos": "git+https://github.com/The-School-of-Athens/Temenos.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^1.18.7"
|
||||
}
|
||||
}
|
11
prisma.yml
Normal file
11
prisma.yml
Normal file
@ -0,0 +1,11 @@
|
||||
endpoint: http://localhost:4466
|
||||
datamodel: datamodel.prisma
|
||||
|
||||
generate:
|
||||
- generator: javascript-client
|
||||
output: ./src/generated/prisma-client/
|
||||
|
||||
hooks:
|
||||
post-deploy:
|
||||
- prisma generate
|
||||
|
403
src/generated/prisma-client/index.d.ts
vendored
Normal file
403
src/generated/prisma-client/index.d.ts
vendored
Normal file
@ -0,0 +1,403 @@
|
||||
// Code generated by Prisma (prisma@1.34.10). DO NOT EDIT.
|
||||
// Please don't change this file manually but run `prisma generate` to update it.
|
||||
// For more information, please read the docs: https://www.prisma.io/docs/prisma-client/
|
||||
|
||||
import { DocumentNode } from "graphql";
|
||||
import {
|
||||
makePrismaClientClass,
|
||||
BaseClientOptions,
|
||||
Model
|
||||
} from "prisma-client-lib";
|
||||
import { typeDefs } from "./prisma-schema";
|
||||
|
||||
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> &
|
||||
U[keyof U];
|
||||
|
||||
export type Maybe<T> = T | undefined | null;
|
||||
|
||||
export interface Exists {
|
||||
user: (where?: UserWhereInput) => Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface Node {}
|
||||
|
||||
export type FragmentableArray<T> = Promise<Array<T>> & Fragmentable;
|
||||
|
||||
export interface Fragmentable {
|
||||
$fragment<T>(fragment: string | DocumentNode): Promise<T>;
|
||||
}
|
||||
|
||||
export interface Prisma {
|
||||
$exists: Exists;
|
||||
$graphql: <T = any>(
|
||||
query: string,
|
||||
variables?: { [key: string]: any }
|
||||
) => Promise<T>;
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
user: (where: UserWhereUniqueInput) => UserNullablePromise;
|
||||
users: (args?: {
|
||||
where?: UserWhereInput;
|
||||
orderBy?: UserOrderByInput;
|
||||
skip?: Int;
|
||||
after?: String;
|
||||
before?: String;
|
||||
first?: Int;
|
||||
last?: Int;
|
||||
}) => FragmentableArray<User>;
|
||||
usersConnection: (args?: {
|
||||
where?: UserWhereInput;
|
||||
orderBy?: UserOrderByInput;
|
||||
skip?: Int;
|
||||
after?: String;
|
||||
before?: String;
|
||||
first?: Int;
|
||||
last?: Int;
|
||||
}) => UserConnectionPromise;
|
||||
node: (args: { id: ID_Output }) => Node;
|
||||
|
||||
/**
|
||||
* Mutations
|
||||
*/
|
||||
|
||||
createUser: (data: UserCreateInput) => UserPromise;
|
||||
updateUser: (args: {
|
||||
data: UserUpdateInput;
|
||||
where: UserWhereUniqueInput;
|
||||
}) => UserPromise;
|
||||
updateManyUsers: (args: {
|
||||
data: UserUpdateManyMutationInput;
|
||||
where?: UserWhereInput;
|
||||
}) => BatchPayloadPromise;
|
||||
upsertUser: (args: {
|
||||
where: UserWhereUniqueInput;
|
||||
create: UserCreateInput;
|
||||
update: UserUpdateInput;
|
||||
}) => UserPromise;
|
||||
deleteUser: (where: UserWhereUniqueInput) => UserPromise;
|
||||
deleteManyUsers: (where?: UserWhereInput) => BatchPayloadPromise;
|
||||
|
||||
/**
|
||||
* Subscriptions
|
||||
*/
|
||||
|
||||
$subscribe: Subscription;
|
||||
}
|
||||
|
||||
export interface Subscription {
|
||||
user: (
|
||||
where?: UserSubscriptionWhereInput
|
||||
) => UserSubscriptionPayloadSubscription;
|
||||
}
|
||||
|
||||
export interface ClientConstructor<T> {
|
||||
new (options?: BaseClientOptions): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
|
||||
export type UserOrderByInput =
|
||||
| "id_ASC"
|
||||
| "id_DESC"
|
||||
| "username_ASC"
|
||||
| "username_DESC"
|
||||
| "password_ASC"
|
||||
| "password_DESC";
|
||||
|
||||
export type MutationType = "CREATED" | "UPDATED" | "DELETED";
|
||||
|
||||
export interface UserCreateInput {
|
||||
username: String;
|
||||
password: String;
|
||||
}
|
||||
|
||||
export interface UserUpdateInput {
|
||||
username?: Maybe<String>;
|
||||
password?: Maybe<String>;
|
||||
}
|
||||
|
||||
export interface UserUpdateManyMutationInput {
|
||||
username?: Maybe<String>;
|
||||
password?: Maybe<String>;
|
||||
}
|
||||
|
||||
export interface UserWhereInput {
|
||||
id?: Maybe<ID_Input>;
|
||||
id_not?: Maybe<ID_Input>;
|
||||
id_in?: Maybe<ID_Input[] | ID_Input>;
|
||||
id_not_in?: Maybe<ID_Input[] | ID_Input>;
|
||||
id_lt?: Maybe<ID_Input>;
|
||||
id_lte?: Maybe<ID_Input>;
|
||||
id_gt?: Maybe<ID_Input>;
|
||||
id_gte?: Maybe<ID_Input>;
|
||||
id_contains?: Maybe<ID_Input>;
|
||||
id_not_contains?: Maybe<ID_Input>;
|
||||
id_starts_with?: Maybe<ID_Input>;
|
||||
id_not_starts_with?: Maybe<ID_Input>;
|
||||
id_ends_with?: Maybe<ID_Input>;
|
||||
id_not_ends_with?: Maybe<ID_Input>;
|
||||
username?: Maybe<String>;
|
||||
username_not?: Maybe<String>;
|
||||
username_in?: Maybe<String[] | String>;
|
||||
username_not_in?: Maybe<String[] | String>;
|
||||
username_lt?: Maybe<String>;
|
||||
username_lte?: Maybe<String>;
|
||||
username_gt?: Maybe<String>;
|
||||
username_gte?: Maybe<String>;
|
||||
username_contains?: Maybe<String>;
|
||||
username_not_contains?: Maybe<String>;
|
||||
username_starts_with?: Maybe<String>;
|
||||
username_not_starts_with?: Maybe<String>;
|
||||
username_ends_with?: Maybe<String>;
|
||||
username_not_ends_with?: Maybe<String>;
|
||||
password?: Maybe<String>;
|
||||
password_not?: Maybe<String>;
|
||||
password_in?: Maybe<String[] | String>;
|
||||
password_not_in?: Maybe<String[] | String>;
|
||||
password_lt?: Maybe<String>;
|
||||
password_lte?: Maybe<String>;
|
||||
password_gt?: Maybe<String>;
|
||||
password_gte?: Maybe<String>;
|
||||
password_contains?: Maybe<String>;
|
||||
password_not_contains?: Maybe<String>;
|
||||
password_starts_with?: Maybe<String>;
|
||||
password_not_starts_with?: Maybe<String>;
|
||||
password_ends_with?: Maybe<String>;
|
||||
password_not_ends_with?: Maybe<String>;
|
||||
AND?: Maybe<UserWhereInput[] | UserWhereInput>;
|
||||
OR?: Maybe<UserWhereInput[] | UserWhereInput>;
|
||||
NOT?: Maybe<UserWhereInput[] | UserWhereInput>;
|
||||
}
|
||||
|
||||
export interface UserSubscriptionWhereInput {
|
||||
mutation_in?: Maybe<MutationType[] | MutationType>;
|
||||
updatedFields_contains?: Maybe<String>;
|
||||
updatedFields_contains_every?: Maybe<String[] | String>;
|
||||
updatedFields_contains_some?: Maybe<String[] | String>;
|
||||
node?: Maybe<UserWhereInput>;
|
||||
AND?: Maybe<UserSubscriptionWhereInput[] | UserSubscriptionWhereInput>;
|
||||
OR?: Maybe<UserSubscriptionWhereInput[] | UserSubscriptionWhereInput>;
|
||||
NOT?: Maybe<UserSubscriptionWhereInput[] | UserSubscriptionWhereInput>;
|
||||
}
|
||||
|
||||
export type UserWhereUniqueInput = AtLeastOne<{
|
||||
id: Maybe<ID_Input>;
|
||||
username?: Maybe<String>;
|
||||
}>;
|
||||
|
||||
export interface NodeNode {
|
||||
id: ID_Output;
|
||||
}
|
||||
|
||||
export interface AggregateUser {
|
||||
count: Int;
|
||||
}
|
||||
|
||||
export interface AggregateUserPromise
|
||||
extends Promise<AggregateUser>,
|
||||
Fragmentable {
|
||||
count: () => Promise<Int>;
|
||||
}
|
||||
|
||||
export interface AggregateUserSubscription
|
||||
extends Promise<AsyncIterator<AggregateUser>>,
|
||||
Fragmentable {
|
||||
count: () => Promise<AsyncIterator<Int>>;
|
||||
}
|
||||
|
||||
export interface BatchPayload {
|
||||
count: Long;
|
||||
}
|
||||
|
||||
export interface BatchPayloadPromise
|
||||
extends Promise<BatchPayload>,
|
||||
Fragmentable {
|
||||
count: () => Promise<Long>;
|
||||
}
|
||||
|
||||
export interface BatchPayloadSubscription
|
||||
extends Promise<AsyncIterator<BatchPayload>>,
|
||||
Fragmentable {
|
||||
count: () => Promise<AsyncIterator<Long>>;
|
||||
}
|
||||
|
||||
export interface UserPreviousValues {
|
||||
id: ID_Output;
|
||||
username: String;
|
||||
password: String;
|
||||
}
|
||||
|
||||
export interface UserPreviousValuesPromise
|
||||
extends Promise<UserPreviousValues>,
|
||||
Fragmentable {
|
||||
id: () => Promise<ID_Output>;
|
||||
username: () => Promise<String>;
|
||||
password: () => Promise<String>;
|
||||
}
|
||||
|
||||
export interface UserPreviousValuesSubscription
|
||||
extends Promise<AsyncIterator<UserPreviousValues>>,
|
||||
Fragmentable {
|
||||
id: () => Promise<AsyncIterator<ID_Output>>;
|
||||
username: () => Promise<AsyncIterator<String>>;
|
||||
password: () => Promise<AsyncIterator<String>>;
|
||||
}
|
||||
|
||||
export interface UserEdge {
|
||||
node: User;
|
||||
cursor: String;
|
||||
}
|
||||
|
||||
export interface UserEdgePromise extends Promise<UserEdge>, Fragmentable {
|
||||
node: <T = UserPromise>() => T;
|
||||
cursor: () => Promise<String>;
|
||||
}
|
||||
|
||||
export interface UserEdgeSubscription
|
||||
extends Promise<AsyncIterator<UserEdge>>,
|
||||
Fragmentable {
|
||||
node: <T = UserSubscription>() => T;
|
||||
cursor: () => Promise<AsyncIterator<String>>;
|
||||
}
|
||||
|
||||
export interface UserSubscriptionPayload {
|
||||
mutation: MutationType;
|
||||
node: User;
|
||||
updatedFields: String[];
|
||||
previousValues: UserPreviousValues;
|
||||
}
|
||||
|
||||
export interface UserSubscriptionPayloadPromise
|
||||
extends Promise<UserSubscriptionPayload>,
|
||||
Fragmentable {
|
||||
mutation: () => Promise<MutationType>;
|
||||
node: <T = UserPromise>() => T;
|
||||
updatedFields: () => Promise<String[]>;
|
||||
previousValues: <T = UserPreviousValuesPromise>() => T;
|
||||
}
|
||||
|
||||
export interface UserSubscriptionPayloadSubscription
|
||||
extends Promise<AsyncIterator<UserSubscriptionPayload>>,
|
||||
Fragmentable {
|
||||
mutation: () => Promise<AsyncIterator<MutationType>>;
|
||||
node: <T = UserSubscription>() => T;
|
||||
updatedFields: () => Promise<AsyncIterator<String[]>>;
|
||||
previousValues: <T = UserPreviousValuesSubscription>() => T;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: ID_Output;
|
||||
username: String;
|
||||
password: String;
|
||||
}
|
||||
|
||||
export interface UserPromise extends Promise<User>, Fragmentable {
|
||||
id: () => Promise<ID_Output>;
|
||||
username: () => Promise<String>;
|
||||
password: () => Promise<String>;
|
||||
}
|
||||
|
||||
export interface UserSubscription
|
||||
extends Promise<AsyncIterator<User>>,
|
||||
Fragmentable {
|
||||
id: () => Promise<AsyncIterator<ID_Output>>;
|
||||
username: () => Promise<AsyncIterator<String>>;
|
||||
password: () => Promise<AsyncIterator<String>>;
|
||||
}
|
||||
|
||||
export interface UserNullablePromise
|
||||
extends Promise<User | null>,
|
||||
Fragmentable {
|
||||
id: () => Promise<ID_Output>;
|
||||
username: () => Promise<String>;
|
||||
password: () => Promise<String>;
|
||||
}
|
||||
|
||||
export interface UserConnection {
|
||||
pageInfo: PageInfo;
|
||||
edges: UserEdge[];
|
||||
}
|
||||
|
||||
export interface UserConnectionPromise
|
||||
extends Promise<UserConnection>,
|
||||
Fragmentable {
|
||||
pageInfo: <T = PageInfoPromise>() => T;
|
||||
edges: <T = FragmentableArray<UserEdge>>() => T;
|
||||
aggregate: <T = AggregateUserPromise>() => T;
|
||||
}
|
||||
|
||||
export interface UserConnectionSubscription
|
||||
extends Promise<AsyncIterator<UserConnection>>,
|
||||
Fragmentable {
|
||||
pageInfo: <T = PageInfoSubscription>() => T;
|
||||
edges: <T = Promise<AsyncIterator<UserEdgeSubscription>>>() => T;
|
||||
aggregate: <T = AggregateUserSubscription>() => T;
|
||||
}
|
||||
|
||||
export interface PageInfo {
|
||||
hasNextPage: Boolean;
|
||||
hasPreviousPage: Boolean;
|
||||
startCursor?: String;
|
||||
endCursor?: String;
|
||||
}
|
||||
|
||||
export interface PageInfoPromise extends Promise<PageInfo>, Fragmentable {
|
||||
hasNextPage: () => Promise<Boolean>;
|
||||
hasPreviousPage: () => Promise<Boolean>;
|
||||
startCursor: () => Promise<String>;
|
||||
endCursor: () => Promise<String>;
|
||||
}
|
||||
|
||||
export interface PageInfoSubscription
|
||||
extends Promise<AsyncIterator<PageInfo>>,
|
||||
Fragmentable {
|
||||
hasNextPage: () => Promise<AsyncIterator<Boolean>>;
|
||||
hasPreviousPage: () => Promise<AsyncIterator<Boolean>>;
|
||||
startCursor: () => Promise<AsyncIterator<String>>;
|
||||
endCursor: () => Promise<AsyncIterator<String>>;
|
||||
}
|
||||
|
||||
/*
|
||||
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
|
||||
*/
|
||||
export type String = string;
|
||||
|
||||
export type Long = string;
|
||||
|
||||
/*
|
||||
The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
|
||||
*/
|
||||
export type ID_Input = string | number;
|
||||
export type ID_Output = string;
|
||||
|
||||
/*
|
||||
The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
|
||||
*/
|
||||
export type Int = number;
|
||||
|
||||
/*
|
||||
The `Boolean` scalar type represents `true` or `false`.
|
||||
*/
|
||||
export type Boolean = boolean;
|
||||
|
||||
/**
|
||||
* Model Metadata
|
||||
*/
|
||||
|
||||
export const models: Model[] = [
|
||||
{
|
||||
name: "User",
|
||||
embedded: false
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Type Defs
|
||||
*/
|
||||
|
||||
export const prisma: Prisma;
|
17
src/generated/prisma-client/index.js
Normal file
17
src/generated/prisma-client/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var prisma_lib_1 = require("prisma-client-lib");
|
||||
var typeDefs = require("./prisma-schema").typeDefs;
|
||||
|
||||
var models = [
|
||||
{
|
||||
name: "User",
|
||||
embedded: false
|
||||
}
|
||||
];
|
||||
exports.Prisma = prisma_lib_1.makePrismaClientClass({
|
||||
typeDefs,
|
||||
models,
|
||||
endpoint: `http://localhost:4466`
|
||||
});
|
||||
exports.prisma = new exports.Prisma();
|
172
src/generated/prisma-client/prisma-schema.js
Normal file
172
src/generated/prisma-client/prisma-schema.js
Normal file
@ -0,0 +1,172 @@
|
||||
module.exports = {
|
||||
typeDefs: // Code generated by Prisma (prisma@1.34.10). DO NOT EDIT.
|
||||
// Please don't change this file manually but run `prisma generate` to update it.
|
||||
// For more information, please read the docs: https://www.prisma.io/docs/prisma-client/
|
||||
|
||||
/* GraphQL */ `type AggregateUser {
|
||||
count: Int!
|
||||
}
|
||||
|
||||
type BatchPayload {
|
||||
count: Long!
|
||||
}
|
||||
|
||||
scalar Long
|
||||
|
||||
type Mutation {
|
||||
createUser(data: UserCreateInput!): User!
|
||||
updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User
|
||||
updateManyUsers(data: UserUpdateManyMutationInput!, where: UserWhereInput): BatchPayload!
|
||||
upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User!
|
||||
deleteUser(where: UserWhereUniqueInput!): User
|
||||
deleteManyUsers(where: UserWhereInput): BatchPayload!
|
||||
}
|
||||
|
||||
enum MutationType {
|
||||
CREATED
|
||||
UPDATED
|
||||
DELETED
|
||||
}
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: String
|
||||
endCursor: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
user(where: UserWhereUniqueInput!): User
|
||||
users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
|
||||
usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection!
|
||||
node(id: ID!): Node
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
user(where: UserSubscriptionWhereInput): UserSubscriptionPayload
|
||||
}
|
||||
|
||||
type User {
|
||||
id: ID!
|
||||
username: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
type UserConnection {
|
||||
pageInfo: PageInfo!
|
||||
edges: [UserEdge]!
|
||||
aggregate: AggregateUser!
|
||||
}
|
||||
|
||||
input UserCreateInput {
|
||||
username: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
type UserEdge {
|
||||
node: User!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
enum UserOrderByInput {
|
||||
id_ASC
|
||||
id_DESC
|
||||
username_ASC
|
||||
username_DESC
|
||||
password_ASC
|
||||
password_DESC
|
||||
}
|
||||
|
||||
type UserPreviousValues {
|
||||
id: ID!
|
||||
username: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
type UserSubscriptionPayload {
|
||||
mutation: MutationType!
|
||||
node: User
|
||||
updatedFields: [String!]
|
||||
previousValues: UserPreviousValues
|
||||
}
|
||||
|
||||
input UserSubscriptionWhereInput {
|
||||
mutation_in: [MutationType!]
|
||||
updatedFields_contains: String
|
||||
updatedFields_contains_every: [String!]
|
||||
updatedFields_contains_some: [String!]
|
||||
node: UserWhereInput
|
||||
AND: [UserSubscriptionWhereInput!]
|
||||
OR: [UserSubscriptionWhereInput!]
|
||||
NOT: [UserSubscriptionWhereInput!]
|
||||
}
|
||||
|
||||
input UserUpdateInput {
|
||||
username: String
|
||||
password: String
|
||||
}
|
||||
|
||||
input UserUpdateManyMutationInput {
|
||||
username: String
|
||||
password: String
|
||||
}
|
||||
|
||||
input UserWhereInput {
|
||||
id: ID
|
||||
id_not: ID
|
||||
id_in: [ID!]
|
||||
id_not_in: [ID!]
|
||||
id_lt: ID
|
||||
id_lte: ID
|
||||
id_gt: ID
|
||||
id_gte: ID
|
||||
id_contains: ID
|
||||
id_not_contains: ID
|
||||
id_starts_with: ID
|
||||
id_not_starts_with: ID
|
||||
id_ends_with: ID
|
||||
id_not_ends_with: ID
|
||||
username: String
|
||||
username_not: String
|
||||
username_in: [String!]
|
||||
username_not_in: [String!]
|
||||
username_lt: String
|
||||
username_lte: String
|
||||
username_gt: String
|
||||
username_gte: String
|
||||
username_contains: String
|
||||
username_not_contains: String
|
||||
username_starts_with: String
|
||||
username_not_starts_with: String
|
||||
username_ends_with: String
|
||||
username_not_ends_with: String
|
||||
password: String
|
||||
password_not: String
|
||||
password_in: [String!]
|
||||
password_not_in: [String!]
|
||||
password_lt: String
|
||||
password_lte: String
|
||||
password_gt: String
|
||||
password_gte: String
|
||||
password_contains: String
|
||||
password_not_contains: String
|
||||
password_starts_with: String
|
||||
password_not_starts_with: String
|
||||
password_ends_with: String
|
||||
password_not_ends_with: String
|
||||
AND: [UserWhereInput!]
|
||||
OR: [UserWhereInput!]
|
||||
NOT: [UserWhereInput!]
|
||||
}
|
||||
|
||||
input UserWhereUniqueInput {
|
||||
id: ID
|
||||
username: String
|
||||
}
|
||||
`
|
||||
}
|
||||
|
15
src/index.js
Normal file
15
src/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
require("dotenv").config();
|
||||
const { ApolloServer } = require('apollo-server');
|
||||
const modules = require("./modules/module-loader");
|
||||
const {schema, context} = modules;
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema,
|
||||
context
|
||||
});
|
||||
|
||||
server
|
||||
.listen({
|
||||
port: 8383
|
||||
})
|
||||
.then(info => console.log(`Server started on http://localhost:${info.port}`));
|
109
src/modules/authentication/index.js
Normal file
109
src/modules/authentication/index.js
Normal file
@ -0,0 +1,109 @@
|
||||
const { GraphQLModule } = require("@graphql-modules/core");
|
||||
const gql = require("graphql-tag");
|
||||
|
||||
const bcrypt = require("bcryptjs");
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
||||
const { prisma } = require('../../generated/prisma-client/index');
|
||||
|
||||
const getUser = token => {
|
||||
try {
|
||||
if(token) {
|
||||
return jwt.verify(token, process.env.JWT_SECRET);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch(err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new GraphQLModule({
|
||||
name: "AUTHENTICATION_MODULE",
|
||||
typeDefs: gql`
|
||||
type User {
|
||||
id: ID!
|
||||
username: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
currentUser: User!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
register(username: String!, password: String!): User!
|
||||
login(username: String!, password: String!): LoginResponse!
|
||||
}
|
||||
|
||||
type LoginResponse {
|
||||
token: String
|
||||
user: User
|
||||
}
|
||||
`,
|
||||
resolvers: {
|
||||
Query: {
|
||||
currentUser: (parent, args, {user, prisma}) => {
|
||||
// Check the authentication
|
||||
if(!user) {
|
||||
throw new Error("Not authenticated");
|
||||
}
|
||||
|
||||
return prisma.user({id: user.id});
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
register: async(parent, {username, password}, ctx, info) => {
|
||||
if(process.env.ENABLE_REGISTRATION === "true") {
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
const user = await ctx.prisma.createUser({
|
||||
username,
|
||||
password: hashedPassword
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
throw new Error("Registration disabled");
|
||||
},
|
||||
login: async(parent, {username, password}, ctx, info) => {
|
||||
console.log(ctx);
|
||||
const user = await ctx.prisma.user({username});
|
||||
|
||||
if(!user) {
|
||||
throw new Error("Invalid login");
|
||||
}
|
||||
|
||||
const passwordMatch = await bcrypt.compare(password, user.password);
|
||||
|
||||
if(!passwordMatch) {
|
||||
throw new Error("Invalid login");
|
||||
}
|
||||
|
||||
const token = jwt.sign (
|
||||
{
|
||||
id: user.id,
|
||||
username: user.email
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
expiresIn: "30d", // So that the token expires in 30 days
|
||||
}
|
||||
)
|
||||
return {
|
||||
token,
|
||||
user
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
context: ({ req }) => {
|
||||
const tokenWithBearer = req.headers.authorization || '';
|
||||
const token = tokenWithBearer.split(' ')[1];
|
||||
const user = getUser(token);
|
||||
|
||||
return {
|
||||
user,
|
||||
prisma
|
||||
}
|
||||
}
|
||||
})
|
78
src/modules/module-loader.js
Normal file
78
src/modules/module-loader.js
Normal file
@ -0,0 +1,78 @@
|
||||
const { GraphQLModule } = require("@graphql-modules/core");
|
||||
const fs = require("fs");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const prisma = require("../generated/prisma-client/index");
|
||||
|
||||
const MODULES_DIRECTORY = "./src/modules/"; // Could be an env variable
|
||||
|
||||
const getUser = token => {
|
||||
try {
|
||||
if(token) {
|
||||
return jwt.verify(token, process.env.JWT_SECRET);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch(err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new GraphQLModule({
|
||||
imports: load_modules()
|
||||
});
|
||||
|
||||
function add_authorization_context(new_module) {
|
||||
new_module._options.context = ({req}) => {
|
||||
const tokenWithBearer = req.headers.authorization || '';
|
||||
const token = tokenWithBearer.split(' ')[1];
|
||||
const user = getUser(token);
|
||||
|
||||
return {
|
||||
user,
|
||||
prisma
|
||||
}
|
||||
};
|
||||
|
||||
return new_module;
|
||||
}
|
||||
|
||||
function is_module_enabled(name) {
|
||||
return process.env[name.toUpperCase()] && process.env[name.toUpperCase()] === "true";
|
||||
}
|
||||
|
||||
function load_modules() {
|
||||
let modules = [];
|
||||
|
||||
let files_list = fs.readdirSync(MODULES_DIRECTORY, {withFileTypes: true});
|
||||
|
||||
for(file of files_list) {
|
||||
if(file.isDirectory() && is_module_enabled(file.name)) {
|
||||
let new_module = require("./" + file.name);
|
||||
if(new_module._options.context === undefined) {
|
||||
new_module = add_authorization_context(new_module);
|
||||
}
|
||||
|
||||
modules.push(new_module);
|
||||
}
|
||||
}
|
||||
|
||||
let installed_modules = process.env.INSTALLED_MODULES.split(',');
|
||||
|
||||
for(module_name of installed_modules) {
|
||||
if(is_module_enabled(module_name)) {
|
||||
let new_module = require(module_name.toLowerCase());
|
||||
if(new_module._options.context === undefined) {
|
||||
new_module = add_authorization_context(new_module);
|
||||
}
|
||||
|
||||
modules.push(new_module);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Loading " + modules.length + " modules");
|
||||
for(module of modules) {
|
||||
console.log("\t - " + module.name);
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
Loading…
Reference in New Issue
Block a user