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