Base setup

This commit is contained in:
Barry Low committed 2018-12-07 19:14:14 -08:00
commit beeccd7e3d
40 files changed
+7288

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
/.cache
.vscode
docs/notes
+46
View File
@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at advancedalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
+45
View File
@@ -0,0 +1,45 @@
# Contributing
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
Advanced Algos LTD is a startup, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regards.
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
# Balanced Contributor Intellectual Property Agreement 1.0.1
This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Advanced Algos LTD ("Company").
**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company.
**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business.
The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects.
**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP.
"Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first.
5. **Voluntary Works** All Company IP that you are involved with or create as part of your work for the Company is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Company to learn more.
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company.
"Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known.
The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your contribution and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
+42
View File
@@ -0,0 +1,42 @@
COPYRIGHT NOTICE
© Copyright 2018 Advanced Algos LTD.
LICENSE NOTICE
Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of
the business are still not clearly defined, we have chosen to --temporarily-- grant a
restrictive license for the use of this repository, until those business aspects are clarified
and a proper open source license can be granted.
Advanced Algos LTD. is publishing the code of this software for several reasons. However,
it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company
in the crypto space and this module is still proprietary software.
The reasons for opening up the code of this module at this stage are the following:
* We believe that transparency is paramount to achieving our goals, so we want everyone
to be able to understand what this module does and how. Opening up the code of this module
allows anyone to take a close look.
* We welcome contributions from the community. Contributions are regarded as voluntary work.
There may be incentives in place for certain kinds of contributions. Contact us to learn more.
Contributors agree to provide an express grant of patent rights for their contributions.
LICENSE WORDING
Advanced Algos LTD. grants a limited license for the following uses:
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
review or audit the code.
2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork this repository
with the sole purpose of correcting bugs and developing functionality in agreement with
Advanced Algos LTD. By contributing to this repository you explicitely agree to the terms
detailed in CONTRIBUTING.MD.
PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
+23
View File
@@ -0,0 +1,23 @@
# Advanced Algos Notifications Module
## Introduction
The general purpose of the notifications module is providing notification services to other AA Platform modules.
## Quick Start
Requirements:
- NodeJS 8+
- NPM (comes installed with NodeJS)
- [Github authorization via SSH](https://help.github.com/articles/connecting-to-github-with-ssh/): *optional* - removes need to authenticate every push or pull from GitHub especially as this is a private repo.
1. Clone this repository `git clone https://github.com/AdvancedAlgos/NotificationsModule.git`
2. `cd NotificationsModule`
5. Navigate to desired package (eg, server) in /packages and look at its README.md for further instructions.
## Getting Started
_This repo is currently under heavy initial-phase development and is not representative of the more mature product. On that note, feel free to look around and keep an eye out as the development unfolds!_
1. Where we set sail — [to the docs!](./docs/README.md)
+18
View File
@@ -0,0 +1,18 @@
# AA Notifications Module Documentation
## Architecture
Currently, this module is only a GraphQL server providing notification services to other platform modules. For example, it wraps the SendGrid API and allows other modules to send transactional emails or provides signup methods to campaign newsletter list.
There is no database or client for this module at this time.
### GraqphQL schema
The Notifications Module graphql schema:
```
type Mutation {
master
teamsSendMemberInvite(email: String!, teamId: String!): String
teamsVerifyMemberInvite(token: String!): String
}
```
+21
View File
@@ -0,0 +1,21 @@
{
"name": "NotificationsModule",
"version": "0.0.1",
"description": "Advanced Algos Notifications Module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/AdvancedAlgos/NotificationsModule.git"
},
"keywords": [
"Advanced Algos"
],
"author": "Barry Low <bearcanru@gmail.com>",
"license": "SEE LICENSE IN LICENSE.MD",
"bugs": {
"url": "https://github.com/AdvancedAlgos/NotificationsModule.git/issues"
},
"homepage": "https://github.com/AdvancedAlgos/NotificationsModule.git#readme"
}
+7
View File
@@ -0,0 +1,7 @@
{
"presets": ["@babel/preset-env"],
"plugins": [
"@babel/plugin-proposal-throw-expressions",
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
+17
View File
@@ -0,0 +1,17 @@
PRESHARED_GATEWAY_KEY=zk8pkrs050fa1pCl
NODE_ENV=development
GENERATE_SOURCEMAP=true
PORT=4101
# SendGrid Api Keys
SG_APIKEY=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
SG_APIKEY_CONTACTS=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
SG_MASTER_SIGNUP_EMAILID=d-b4c1424b7b444fdab69e2c8d19f17497
SG_MASTER_FEEDBACK_EMAILID=d-c613efbcb0484124a1d49cf9c11de07b
SG_CORPORATE_SIGNUP_EMAILID=d-b4c1424b7b444fdab69e2c8d19f17497
SG_CORPORATE_CONTACT_EMAILID=sg-master-contact-template-id
SG_TEAMS_MEMBER_INVITE_EMAILID=sg-teams-member-invite-template-id
+19
View File
@@ -0,0 +1,19 @@
PORT=4101
PRESHARED_GATEWAY_KEY=SECRET_STUFF
NODE_ENV=development
GENERATE_SOURCEMAP=false
# SendGrid Api Keys
SG_APIKEY=sg-apikey
SG_APIKEY_CONTACTS=sg-apikey-contacts
# SendGrid Template IDs
SG_MASTER_SIGNUP_EMAILID=sg-master-signup-template-id
SG_MASTER_FEEDBACK_EMAILID=sg-master-feedback-template-id
SG_CORPORATE_SIGNUP_EMAILID=sg-master-signup-template-id
SG_CORPORATE_CONTACT_EMAILID=sg-master-contact-template-id
SG_TEAMS_MEMBER_INVITE_EMAILID=sg-teams-member-invite-template-id
+12
View File
@@ -0,0 +1,12 @@
/.env
/.env.prod
/database/.env
/database/docker-compose.prisma.prod.yml
/docker-compose.prisma.prod.yml
/docker-compose.prod.yml
.graphqlconfig.prod.yml
dist
node_modules
.idea
.vscode
*.log
+12
View File
@@ -0,0 +1,12 @@
projects:
app:
schemaPath: src/schema.graphql
extensions:
endpoints:
dev1:
url: http://localhost:4001/graphql
database:
schemaPath: src/generated/prisma.graphql
extensions:
prisma: database/prisma.yml
+46
View File
@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at advancedalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
+45
View File
@@ -0,0 +1,45 @@
# Contributing
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
Advanced Algos LTD is a startup, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regards.
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
# Balanced Contributor Intellectual Property Agreement 1.0.1
This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Advanced Algos LTD ("Company").
**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company.
**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business.
The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects.
**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP.
"Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first.
5. **Voluntary Works** All Company IP that you are involved with or create as part of your work for the Company is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Company to learn more.
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company.
"Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known.
The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your contribution and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
+23
View File
@@ -0,0 +1,23 @@
FROM mhart/alpine-node:8
ENV PORT=4101
RUN mkdir -p /var/www/app && \
mkdir -p /var/www/app/node_modules
# Use app directory as current workdir
WORKDIR /var/www/app
ENV PATH /var/www/app/node_modules/.bin:$PATH
# Copy package.json to workdir
COPY package.json /var/www/app/package.json
# Install dependencies
RUN npm install
RUN npm audit fix
# Copy application code
COPY . /var/www/app
# Expose Port
EXPOSE $PORT
+42
View File
@@ -0,0 +1,42 @@
COPYRIGHT NOTICE
© Copyright 2018 Advanced Algos LTD.
LICENSE NOTICE
Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of
the business are still not clearly defined, we have chosen to --temporarily-- grant a
restrictive license for the use of this repository, until those business aspects are clarified
and a proper open source license can be granted.
Advanced Algos LTD. is publishing the code of this software for several reasons. However,
it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company
in the crypto space and this module is still proprietary software.
The reasons for opening up the code of this module at this stage are the following:
* We believe that transparency is paramount to achieving our goals, so we want everyone
to be able to understand what this module does and how. Opening up the code of this module
allows anyone to take a close look.
* We welcome contributions from the community. Contributions are regarded as voluntary work.
There may be incentives in place for certain kinds of contributions. Contact us to learn more.
Contributors agree to provide an express grant of patent rights for their contributions.
LICENSE WORDING
Advanced Algos LTD. grants a limited license for the following uses:
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
review or audit the code.
2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork this repository
with the sole purpose of correcting bugs and developing functionality in agreement with
Advanced Algos LTD. By contributing to this repository you explicitely agree to the terms
detailed in CONTRIBUTING.MD.
PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
+114
View File
@@ -0,0 +1,114 @@
# Advance Algos Team GraphQL API Server
This is the Advanced Algos Team Module server-side GraphQL API server. We use Webpack 4 + Docker to develop locally, then build to a distribution bundle that can then be uploaded via FTP, git or used with a continuous integration workflow.
This modules client-side component is the [aateam-client package](../aateam-client#README).
For more information on the general architecture and related technology of this module, please visit this repos [main documenation](../../docs#README).
## Getting Started
Run
```
npm install
```
then
```
npm run dev
```
### Using Docker
Get latest Docker and Docker Compose: [https://docs.docker.com/compose/install/](https://docs.docker.com/compose/install/)
#### Using Docker container for Development
To run starter kit in development mode with hot code reload execute:
Run docker container in background:
```
docker-compose up -d
```
If image issues, try running:
```
docker-compose up --build
```
then run
```
docker-compose up -d
```
To view logs
```
docker-compose logs -f -t
```
Hit Ctrl + c to exit logs without stopping service
To stop service
```
docker-compose down
```
## Overview
The development architecture of the Algobots Community site is to allow both rapid initial development and ongoing rapid-evolution. As with any opinionated workflow, there's a learning curve, but the libraries and architecture of this application have been chosen to maximize collaboration, flexibility, and extensibility.
### Main Libraries
- [*Webpack 4*](https://webpack.js.org/):web application bundler. Allows us to us latest newer JS ECMA features that increase productivity and code clarity. Also allows additional libraries such as modular and programmable CSS via SASS/SCSS/LESS. Provides webserver for local development.
- [*Bulma CSS Framework*](https://bulma.io/documentation/): Bulma is a highly-customizable CSS-only framework allowing us to quickly prototype the layout of a webapp without being complicated by opinionated functionality represented in libraries such as Bootstrap or Material UI. Grants us the flexibility to create a custom look at top speed whilst minimizing baggage.
- [*HyperApp*](https://github.com/hyperapp/hyperapp): Allows a controlled Flux-styled approach to state-management and data-flow through components like React minus the excess boilerplate and learning curve.
- [*Docker*](https://www.docker.com/what-docker)
### File organization
```
. # Top level directory located at your choice
├── src
│ ├── actions # Actions are functions that change the app state
│ ├── assets # images, logos and other static assets
│ ├── state # Schema of state — the single source of truth concerning app state.
│ ├── styles # SASS/SCSS styles
│ ├── utils # Local storage and other utils
│ └── views # Main folder for editing site content
│ ├─ nav # Nav components — header, footer, etc.
│ ├─ pages # Nav components — header, footer, etc.
│ │ ├─ landing # Homepage
│ │ │ ├─ sections # Homepage sections. Collated in its own index.js
│ │ │ └─ index.js # Main homepage component. Pulls in sections
│ │ └─ ... # Other page/component folders
│ └─ index.js # Main view container. Main router
├── html
│ └── index.html # index.html template pulled in by Webpack to attach transpiled bundles
├── index.js # JS entry point to website
│
├── tools # utility scripts, mostly for webpack configuration
│
├── node_modules # created by running `npm install` in this packages root dir
├── dist # created by `npm run develop` - a cache of bundled files served by Webpack for local development
└── build # created by `npm run build` - location of deployable production bundle
```
## Deployment
The simplest deployment is compiling/transpiling the site to a distribution bundle and then uploading the files in the bundle to a web-accessible folder or storage.
For this application we use Webpack to transpile the application which combines and minimizes all code and assets into an optimized bundle without and of the development code and resources.
To build, run:
```
npm run build
```
Looking in this packages directory, there should be a ./build directory with the final built files that can be uploaded.
### Create Docker image
```
docker build -t aateamapi/prod .
```
### Deployment instructions
Coming soon...
+19
View File
@@ -0,0 +1,19 @@
version: '3'
services:
api:
build:
context: .
dockerfile: Dockerfile
env_file: ./.env.dev
container_name: aanotificationapi
ports:
- "4101:4101"
volumes:
- .:/var/www/app
- /var/www/app/node_modules
tty: true
stdin_open: true
volumes:
working_dir: /var/www/app
restart: always
command: node --require @babel/register --require dotenv/config src/index.js
+5830
View File
File diff suppressed because it is too large. Load diff
+42
View File
@@ -0,0 +1,42 @@
{
"name": "aa-notifications-server",
"scripts": {
"start": "NODE_ENV=production nodemon -e js,graphql -x node -r dotenv/config src/index.js",
"debug": "nodemon src/index.js --watch ./src --exec \"node --require @babel/register --require dotenv/config\"",
"playground": "graphql playground --dotenv dotenv/config",
"dev": "npm-run-all --parallel debug playground"
},
"dependencies": {
"@sendgrid/client": "^6.3.0",
"@sendgrid/mail": "^6.3.1",
"apollo-errors": "^1.9.0",
"apollo-server-express": "^2.2.6",
"axios": "^0.18.0",
"cors": "^2.8.5",
"express": "^4.16.4",
"fast-xml-parser": "^3.12.10",
"graphql-import": "^0.7.1",
"graphql-middleware": "^2.0.2",
"graphql-subscriptions": "^1.0.0",
"graphql-tag": "^2.10.0",
"graphql-tools": "^4.0.3",
"jsonwebtoken": "^8.4.0",
"lodash.get": "^4.4.2",
"lodash.merge": "^4.6.1",
"request": "^2.88.0",
"request-promise-native": "^1.0.5"
},
"devDependencies": {
"@babel/cli": "^7.2.0",
"@babel/core": "^7.2.0",
"@babel/node": "^7.2.0",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "^7.2.0",
"@babel/register": "^7.0.0",
"dotenv": "^6.2.0",
"nodemon": "^1.18.7",
"winston": "^3.1.0"
}
}
+130
View File
@@ -0,0 +1,130 @@
import http from 'http'
import { ApolloServer, makeExecutableSchema } from 'apollo-server-express'
import { applyMiddleware as applyGraphQLMiddleware } from 'graphql-middleware'
import { moduleAuth } from './middleware'
import { logger } from './logger'
/**
*
* @description Creates an Apollo Server. Setup based on `vue-cli-plugin-apollo`.
* @param {Object} app Express application
* @param {Object} options Apollo options
* @returns {Object} HTTP Server
*/
export default function createApolloServer(
app,
{
// Main options
graphqlEndpoint = '',
typeDefs = {},
resolvers = {},
schemaDirectives = {},
directiveResolvers = {},
graphqlMiddlewares = [],
dataSources = () => ({}),
context = () => ({}),
// Subscriptions
subscriptionsEndpoint = '',
wsMiddlewares = [],
// Mocks
mocks,
// Apollo Engine
engineKey = '',
// HTTP options
cors = true,
timeout = 120000,
// Extra options for Apollo Server
apolloServerOptions = {},
}
) {
const schema = makeExecutableSchema({
typeDefs,
resolvers,
schemaDirectives,
directiveResolvers,
resolverValidationOptions: {
requireResolversForResolveType: false,
},
})
// Apollo server options
const options = {
...apolloServerOptions,
schema: applyGraphQLMiddleware(schema, ...graphqlMiddlewares),
tracing: true,
cacheControl: true,
introspection: true,
engine: engineKey ? { apiKey: engineKey } : false,
dataSources,
// Resolvers context in POST requests
context: async ({ req, connection }) => {
let contextData
try {
if (connection) {
contextData = { ...connection.context }
} else {
contextData = await context({ req, request: req })
}
} catch (err) {
logger.error(err)
throw err
}
return contextData
},
// Resolvers context in WebSocket requests
subscriptions: {
path: subscriptionsEndpoint,
onConnect: async (connection, websocket) => {
const { authorization, userid } = connection
let contextData = {}
try {
// Simulate `req` object for auth
const req = { headers: { authorization, userid } }
// Call all middlewares in order and modify `req`
await new Promise((resolve, reject) =>
wsMiddlewares.reduceRight(
(acc, m) => err => (err ? reject(err) : m(req, null, acc)),
err => (err ? reject(err) : resolve())
)()
)
contextData = await context({
connection,
websocket,
request: req,
req,
})
} catch (err) {
logger.error(err)
}
return contextData
},
},
}
// Apollo Server
const server = new ApolloServer(options)
// Express middleware
server.applyMiddleware({
moduleAuth,
app,
cors,
path: graphqlEndpoint,
// gui: {
// endpoint: graphqlEndpoint,
// subscriptionEndpoint: graphqlSubscriptionsPath,
// },
})
// Create HTTP server and add subscriptions
const httpServer = http.createServer(app)
httpServer.setTimeout(timeout)
server.installSubscriptionHandlers(httpServer)
return httpServer
}
+153
View File
@@ -0,0 +1,153 @@
var jwt = require('jsonwebtoken')
var axios = require('axios')
import { logger } from '../logger'
const sendTeamMemberInvite = function(email, team) {
const dev = process.env.NODE_ENV === 'development' ? true : false
logger.info(`sendTeamMemberInvite email|team: ${email} | ${team}`)
const API_KEY = process.env.SG_APIKEY
const token = jwt.sign({ email: email, team: team.slug }, API_KEY, { expiresIn: '7d' })
let origin = 'https://teams.advancedalgos.net'
const params = '/activate-team-membership/'
if (dev){
origin = 'http://localhost:3001'
}
const headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : origin,
'Access-Control-Allow-Credentials': 'true'
};
const data = JSON.stringify({
"personalizations": [
{
"to": [
{
"email": email
}
],
dynamic_template_data: {
"aateaminvitelink": origin + params + token,
"aateamname": team.name,
"subject": "INVITATION: Join team " + team.name
},
"subject": "INVITATION: Join team " + team.name
}
],
"from": {
"email": "noreply@advancedalgos.net",
"name": "Advanced Algos Teams"
},
"reply_to": {
"email": "feedback@advancedalgos.net",
"name": "Advanced Algos Teams"
},
"template_id": process.env.SG_INVITE_EMAILID
})
let sendVerify = axios({
method: 'post',
url: 'https://api.sendgrid.com/v3/mail/send',
data: data,
headers:{
'content-type': 'application/json',
'authorization': 'Bearer ' + API_KEY
}
})
.then(function (response) {
if (response.status >= 200 && response.status < 300) {
return 'Success'
} else {
throw response.data.errors[0].message
}
})
.catch(function (error) {
return `${error.status}-${error.response.data.errors[0].message}`
})
return sendVerify
}
const sendTeamCreateConfirmation = function(email, teamName, botName) {
const dev = process.env.NODE_ENV === 'development' ? true : false
logger.info(`sendTeamCreateConfirmation email|teamName|botName: ${email} | ${teamName} | ${botName}`)
const API_KEY = process.env.SG_APIKEY
let origin = 'https://teams.advancedalgos.net'
if (dev){
origin = 'http://localhost:3001'
}
const headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : origin,
'Access-Control-Allow-Credentials': 'true'
};
const data = JSON.stringify({
"personalizations": [
{
"to": [
{
"email": email
}
],
dynamic_template_data: {
"aadeveloplink": "https://develop.advancedalgos.net/index.html",
"aateamname": teamName,
"aabotname": botName,
"subject": "Team " + teamName + " has been created!"
},
"subject": "Team " + teamName + " has been created!"
}
],
"from": {
"email": "noreply@advancedalgos.net",
"name": "Advanced Algos Teams"
},
"reply_to": {
"email": "feedback@advancedalgos.net",
"name": "Advanced Algos Teams"
},
"template_id": process.env.SG_TEAMCREATE_EMAILID
})
let sendTeamCreation = axios({
method: 'post',
url: 'https://api.sendgrid.com/v3/mail/send',
data: data,
headers:{
'content-type': 'application/json',
'authorization': 'Bearer ' + API_KEY
}
})
.then(function (response) {
if (response.status >= 200 && response.status < 300) {
return 'Success'
} else {
throw response.data.errors[0].message
}
})
.catch(function (error) {
return `${error.status}-${error.response.data.errors[0].message}`
})
return sendTeamCreation
}
const verifyInviteToken = async function(token) {
const API_KEY = process.env.SG_APIKEY
let verifiedToken
try {
verifiedToken = await jwt.verify(token, API_KEY, {maxAge: '7d'})
} catch(err) {
if (err.name === "TokenExpiredError"){
throw new Error ("Error: Token Expired. Please request new invitation or apply to the team again")
} else {
throw new Error (`Error: Token error - ${err.message}`)
}
}
logger.info(`verifyInviteToken: ${verifiedToken}`)
return verifiedToken
}
module.exports = { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken }
+49
View File
@@ -0,0 +1,49 @@
import { ApolloError } from 'apollo-server-express'
import { inspect } from 'util'
import { logger } from '../logger'
export {
ApolloError,
AuthenticationError,
ForbiddenError,
SyntaxError,
UserInputError,
ValidationError,
} from 'apollo-server-express'
export const formatError = (error) => {
// This can be used to send errors to third
// party services like Sentry or Stackdriver
logger.info('formatError')
logger.error(JSON.parse(JSON.stringify(inspect(error))))
const originalError = searchOriginalError(error)
logger.error('originalError')
logger.error(originalError)
const { message, code } = originalError
return { message, code}
}
const searchOriginalError = (error) => {
if (error.originalError) {
logger.error('Original Error')
logger.error(error.originalError)
return error.originalError
}
if (error.errors) {
logger.error('Errors')
logger.error(JSON.parse(JSON.stringify(inspect(error.errors))))
return error.errors.map(searchOriginalError)[0]
}
logger.error('searchOriginalErrors')
logger.error(JSON.parse(JSON.stringify(inspect(error))))
return error;
}
// Define custom Apollo errors here
export class ExternalServiceError extends ApolloError {
constructor(message) {
super(message, 'EXTERNAL_SERVICE_UNAVAILABLE')
Object.defineProperty(this, 'name', { value: this.constructor.name })
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"errors": [
{
"code": 401,
"message": "Wait, you're not supposed to be here, go through the gate as everyone else"
}
]
}
@@ -0,0 +1,17 @@
export const createMember = async function (ctx, info, authId) {
logger.info('createMember', authId)
const member = await ctx.db.mutation.upsertMember({
where: {
authId: authId,
},
create: {
authId: authId,
alias: idToken.nickname
},
update: {
authId: authId,
alias: idToken.nickname
}
}, info)
return member
}
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,188 @@
import axios from 'axios'
import { ApolloError } from 'apollo-server-express'
import { getUser } from '../../../middleware/getMember'
import { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } from '../../../email/sendgrid'
import TEAMS_CONNECTIONS_FRAGMENT from '../../fragments/TeamsConnectionsFragment'
import TEAMS_FRAGMENT from '../../fragments/TeamsFragment'
import TEAM_FB_FRAGMENT from '../../fragments/TeamFBFragment'
import { logger, AuthenticationError, ServiceUnavailableError, DatabaseError, ConflictError } from '../../../logger'
export const resolvers = {
Mutation: {
async sendMemberInviteSG(parent, { email, teamId }, ctx, info) {
const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT)
const addMember = await ctx.db.mutation.upsertTeamMembers({
where:{email: email},
create:{role:'MEMBER', email:email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invited by ${team.members[0].member.alias}`}}},
update:{role: 'MEMBER', email: email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invite resent by ${team.members[0].member.alias}`}}}},
`{ id }`)
const sendInvite = await sendTeamMemberInvite(email, team)
return sendInvite
},
async verifyTeamInvite(parent, { token }, ctx, info) {
let verifiedToken = null
let team = null
try {
verifiedToken = await verifyInviteToken(token)
team = {
email: verifiedToken.email,
team: {
slug: verifiedToken.team
}
}
logger.info('verifyTeamInvite.token: ', await verifiedToken)
} catch (err) {
logger.debug('verifyTeamInvite. err: ', err)
throw new DatabaseError(err.message)
}
return team
},
async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) {
logger.info('createTeam ctx.request.user:')
logger.info(ctx.request.headers.userid)
const authId = ctx.request.headers.userid
if (!authId) {
throw new AuthenticationError()
}
const userData = await getUser(authId)
logger.info('userData:')
logger.info(JSON.stringify(await userData))
const alias = await userData.data.users_User.alias
const email = await userData.data.users_User.email
const avatar = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-avatar-default.png'
const banner = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-banner-default.png'
const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}/${botName}`)
logger.info('createTeamUrl:')
logger.info(JSON.stringify(await createTeamUrl))
const platformUrl = 'https://charts-dev.advancedalgos.net/AABrowserAPI/newTeam/'
// const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/'
logger.info(`${platformUrl}${createTeamUrl}/${authId}`)
const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}/${process.env.AAWEB_TEAM_SHARED_SECRET}`)
.then((result) => {
console.log('createPlatformTeam result:', result.data)
if(result.data.message === 'Team Name already taken'){
return { error: 'Team already exist on AA Cloud.', code: 409 }
}
if(result.data.result === 'Fail'){
return { error: `${result.data.message} - Creating team on AA Cloud has failed`, code: 404 }
}
return result
})
logger.info('createPlatformTeam')
logger.info(await createPlatformTeam)
logger.info('createPlatformTeam Error')
logger.error(await createPlatformTeam.error)
logger.info('createPlatformTeam Code')
logger.error(await createPlatformTeam.code)
// if (await createPlatformTeam.error && await createPlatformTeam.code === 404) throw new ApolloError(await createPlatformTeam.error, 404)
// if (await createPlatformTeam.error && await createPlatformTeam.code === 409) throw new ApolloError(await createPlatformTeam.error, 409)
const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`)
.catch((err) => {
logger.debug(err, 'existingMember error: ')
throw new DatabaseError(err)
})
logger.debug('existingMember:')
logger.debug(await existingMember)
let createTeam
if(existingMember !== null && existingMember.id !== null){
logger.info('createTeam with existingMember:')
createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Connected to Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT)
.catch((err) => {
logger.debug(err, 'createTeamMutation error: ')
return new DatabaseError(err)
})
} else {
logger.info('createTeam without existingMember:')
createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {create: {authId: authId, alias: alias, visible:'true', status: { create: { status: 'ACTIVE', reason: `Created team ${name}`}}}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Created with Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT)
.catch((err) => {
logger.debug(err, 'createTeamMutation error: ')
return new DatabaseError(err)
})
}
sendTeamCreateConfirmation(email, name, botName)
return createTeam
},
async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) {
return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT)
.catch((err) => {
logger.debug('createTeam error: ', err)
throw new DatabaseError(`Team Profile Update Error: ${err}`)
})
},
async updateFB(parent, { fbId, avatar}, ctx, info) {
return ctx.db.mutation.updateFinancialBeings({data:{avatar:avatar}, where:{id:fbId}}, info)
.catch((err) => {
logger.debug('createTeam error: ', err)
throw new DatabaseError(`Update Financial Being Error: ${err}`)
})
},
async deleteTeam(parent, { slug, botSlug }, ctx, info) {
logger.info('deleteTeam ctx.request.user:')
logger.info(ctx.request.headers.userid)
const authId = ctx.request.headers.userid
if (!authId) {
throw new AuthenticationError()
}
logger.info(authId)
logger.info('getUser')
logger.info(getUser)
return getUser(authId)
.then(async result => {
logger.info('deleteTeam axios:')
logger.info(await result)
const alias = await result.data.users_User.alias
// const email = result.data.users_User.email
const deleteTeamUrl = encodeURI(`${slug}/${alias}/${botSlug}`)
logger.info('deleteTeamUrl:')
logger.info(JSON.stringify(await deleteTeamUrl))
const platformUrl = 'https://charts-dev.advancedalgos.net/AABrowserAPI/deleteTeam/'
// const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/'
logger.info(`${platformUrl}${deleteTeamUrl}/${authId}`)
const deletePlatformTeam = await axios.get(`${platformUrl}${deleteTeamUrl}/${authId}/${process.env.AAWEB_DELETE_TEAM_SHARED_SECRET}`)
.then((result) => {
console.log('deletePlatformTeam result:', result.data)
if(result.data.result === 'Fail'){
throw new DatabaseError(result.data.message)
}
return result
})
.catch(err =>{
logger.debug('deletePlatformTeam err:')
logger.debug(err)
throw new DatabaseError(err)
})
console.log('deletePlatformTeam returned: ', await deletePlatformTeam)
return ctx.db.mutation.deleteTeam({ where: { slug } }, info)
.catch((res) => {
logger.info('deleteTeam error: ', res)
const errors = res.graphQLErrors.map((error) => {
throw new DatabaseError(error.message)
})
})
})
.catch((err) => {
logger.debug('deleteTeam error: ')
throw new DatabaseError(err)
})
}
}
}
Whitespace-only changes.
@@ -0,0 +1,20 @@
import { logger } from '../../../logger'
import { member, owner } from './Member'
import { teams, teamById, teamByName, teamBySlug, teamWithRole, teamsByOwner, teamsByRole } from './Team'
import{ fbByTeamMember } from './TeamMember'
export const resolvers = {
Query: {
member: (parent, arg, ctx, info) => member(parent, arg, ctx, info),
teams: (parent, arg, ctx, info) => teams(parent, arg, ctx, info),
teamById: (parent, arg, ctx, info) => teamById(parent, arg, ctx, info),
teamByName: (parent, arg, ctx, info) => teamByName(parent, arg, ctx, info),
teamBySlug: (parent, arg, ctx, info) => teamBySlug(parent, arg, ctx, info),
teamWithRole: (parent, arg, ctx, info) => teamWithRole(parent, arg, ctx, info),
teamsByOwner: (parent, arg, ctx, info) => teamsByOwner(parent, arg, ctx, info),
teamsByRole: (parent, arg, ctx, info) => teamsByRole(parent, arg, ctx, info),
owner: (parent, arg, ctx, info) => owner(parent, arg, ctx, info),
fbByTeamMember: (parent, arg, ctx, info) => fbByTeamMember(parent, arg, ctx, info)
}
}
@@ -0,0 +1,57 @@
import axios from 'axios'
import { logger, DatabaseError } from '../logger'
export const getMember = async (req, res, next, db) => {
if (!req.headers.userId) return next()
logger.info('getMember ')
logger.info(req.headers.userId)
try {
const member = await getUser(req.headers.userId)
req.user = await member
next()
} catch (err) {
next()
}
}
export const getUser = authId => {
logger.info('getUser MiddleWare')
logger.info(authId)
const API_URL = 'https://app-api.advancedalgos.net/graphql'
return new Promise((resolve, reject) => {
try {
return axios.post(API_URL, {
query: `query users_User($userId: ID!) {
users_User(id: $userId){
id
alias
email
}
}`,
variables: {
userId: authId
}
}, {
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
logger.info('getUser result')
logger.info(res.data)
resolve(res.data)
})
.catch(err => {
logger.info('getUser err')
logger.info(err)
reject(err)
})
} catch (error) {
reject(error)
}
})
}
export default getUser
+6
View File
@@ -0,0 +1,6 @@
import merge from 'lodash.merge'
// import { resolvers as queryResolvers } from './Query'
import { resolvers as mutationResolvers } from './Mutation'
export const resolvers = merge(mutationResolvers)
+48
View File
@@ -0,0 +1,48 @@
# import Query.TeamConnection, Team from "../generated/prisma.graphql"
directive @isAuthenticated on QUERY | FIELD | MUTATION
directive @hasRole(roles: [String]) on QUERY | FIELD | MUTATION
directive @isOwner(type: String) on QUERY | MUTATION
directive @isOwnerOrHasRole(type: String, roles: [String]) on QUERY | MUTATION
type Query {
teams: TeamConnection
teamDetails: [Team!]! @isAuthenticated
teamByName(name: String!): Team
teamBySlug(slug: String!): Team
teamById(teamId: String!): Team
teamWithRole(teamId: String!, role: String!): Team
teamsByOwner: [Team!]! @isAuthenticated
teamsByRole: [Team!]! @isAuthenticated
fbByTeamMember: Team @isAuthenticated
owner: Member @isAuthenticated
member: Member @isAuthenticated
}
type Mutation {
authenticate(idToken: String!): Member
verifyTeamInvite(token: String!): TeamMembers
createTeam(name: String!, slug: String!, botName: String!, botSlug: String!): Team @isAuthenticated
updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated
updateFB(fbId: String!, avatar: String): FinancialBeings @isAuthenticated
deleteTeam(slug: String!, botSlug: String!): Team @isAuthenticated @isOwner(type: "Team")
sendMemberInviteSG(email: String!, teamId: String!): String
confirmMember(token: String!): String
removeTeamMember(teamId: String!, memberId: String!): String
}
fragment MemberInfo on Member {
id
authId
alias
visible
status {
...MemberStatusInfo
}
}
fragment MemberStatusInfo on MemberStatus {
status
reason
createdAt
}
+87
View File
@@ -0,0 +1,87 @@
import '@babel/polyfill'
require('dotenv').config();
import express from 'express'
import createApolloServer from './ApolloServer'
import { formatError } from './errors'
import {
Prisma,
extractFragmentReplacements,
forwardTo
} from 'prisma-binding'
import { makeExecutableSchema } from 'graphql-tools'
import { importSchema } from 'graphql-import'
import parser from 'fast-xml-parser'
import axios from 'axios'
import { resolvers } from './graphql/resolvers'
import { schemaDirectives } from './directives'
import pubsub from './pubsub'
import { logger } from './logger'
import wrongPreshared from './errors/notAllowed.json'
const GRAPHQL_ENDPOINT = '/graphql'
const GRAPHQL_SUBSCRIPTIONS = '/graphql'
const PORT = 4001
const NODE_ENV = 'development'
const db = new Prisma({
fragmentReplacements: extractFragmentReplacements(resolvers),
typeDefs: 'src/generated/prisma.graphql',
endpoint: process.env.PRISMA_ENDPOINT,
secret: process.env.PRISMA_SECRET,
debug: false
})
const app = express()
app.post(GRAPHQL_ENDPOINT, (req, res, next) => {
if (req.headers.preshared === process.env.PRESHARED_GATEWAY_KEY) {
next();
} else {
res.send(wrongPreshared);
}
});
app.post(GRAPHQL_ENDPOINT)
const corsOptions = {
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}
const options = {
port: 4001,
cors: corsOptions,
endpoint: '/graphql',
subscriptions: '/graphql',
playground: '/graphiql'
}
const server = createApolloServer(app, {
graphqlEndpoint: GRAPHQL_ENDPOINT,
subscriptionsEndpoint: GRAPHQL_SUBSCRIPTIONS,
apolloServerOptions: { formatError },
typeDefs: importSchema('src/graphql/schema.graphql'),
resolvers,
resolverValidationOptions: { requireResolversForResolveType: false },
schemaDirectives,
context: req => ({
...req ,
token: req.headers ? req.headers : undefined,
user: req.user ? req.user : undefined,
userId: (req.headers && req.headers.userid) ? req.headers.userid : undefined,
db,
pubsub
})
})
server.listen({ port: PORT }, () => {
logger.info(
`🚀 GraphQL Server is running on http://localhost:${PORT}${GRAPHQL_ENDPOINT} in "${NODE_ENV}" mode\n`
)
})
+24
View File
@@ -0,0 +1,24 @@
export class AuthenticationError extends Error {
code = 401
message = 'Not Authenticated - you have to be authenticated to perform this action'
}
export class DatabaseError extends Error {
code = 404
message = `Resource Error: ${this.message}`
}
export class ApolloError extends Error {
code = 409
message = `Resource already exists: ${this.message}`
}
export class WrongArgumentsError extends Error {
code = 400
message = 'Wrong arguments : ' + this.message
}
export class ServiceUnavailableError extends Error {
code = 503
message = 'At least one service is unresponding ' + this.message
}
+33
View File
@@ -0,0 +1,33 @@
import { createLogger, format, transports } from 'winston'
import { inspect } from 'util'
import { AuthenticationError, ApolloError, DatabaseError, WrongArgumentsError, ServiceUnavailableError, ConflictError } from './errors'
class LoggerService {
constructor() {
const winstonTransports = [
new transports.Console({
format: format.combine(
format.colorize(),
format.timestamp({ format: 'YY/MM/DD HH:mm' }),
format.printf(
info => `${info.timestamp} - ${info.level}: ${JSON.parse(JSON.stringify(inspect(info.message)))}`
)
),
}),
]
const logger = createLogger({
level: 'info',
transports: winstonTransports,
exceptionHandlers: winstonTransports,
})
return new Proxy(this, {
get: (target, propKey) => logger[propKey],
})
}
}
export const logger = new LoggerService() // Default logger
export { LoggerService, AuthenticationError, ApolloError, ConflictError, DatabaseError, WrongArgumentsError, ServiceUnavailableError }
+23
View File
@@ -0,0 +1,23 @@
import axios from 'axios'
import { logger, DatabaseError } from '../logger'
export const getMember = async (req, res, next, db) => {
if (!req.headers.userId) return next()
logger.debug('getMember ')
logger.debug(req.headers.userId)
try {
const member = await getUser(req.headers.userId)
req.user = await member
logger.debug('getMember - getUser:')
logger.debug(req.user)
next()
} catch (err) {
logger.debug('getMember - error:')
logger.debug(err)
next()
}
}
export default getMember
+3
View File
@@ -0,0 +1,3 @@
import moduleAuth from './moduleAuth'
export { moduleAuth }
+13
View File
@@ -0,0 +1,13 @@
import { logger } from '../logger'
export const moduleAuth = async (req, res, next, db) => {
if (req.headers.preshared === process.env.PRESHARED_GATEWAY_KEY) {
logger.info('Module authenticated')
next();
} else {
logger.error('Module authentication error')
res.send(wrongPreshared);
}
}
export default moduleAuth
+3
View File
@@ -0,0 +1,3 @@
import { PubSub } from 'graphql-subscriptions'
export default new PubSub()