├── .gitignore
├── angular-client
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── codegen.yml
├── e2e
│ ├── protractor.conf.js
│ ├── src
│ │ ├── app.e2e-spec.ts
│ │ └── app.po.ts
│ └── tsconfig.e2e.json
├── package.json
├── src
│ ├── app
│ │ ├── accounts
│ │ │ ├── accounts.module.ts
│ │ │ ├── can-activate-auth.service.spec.ts
│ │ │ └── can-activate-auth.service.ts
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── graphql
│ │ │ └── graphql.module.ts
│ │ ├── home
│ │ │ ├── AddPost.graphql
│ │ │ ├── AllPosts.graphql
│ │ │ ├── home.component.html
│ │ │ ├── home.component.scss
│ │ │ ├── home.component.spec.ts
│ │ │ └── home.component.ts
│ │ ├── login
│ │ │ ├── login.component.html
│ │ │ ├── login.component.scss
│ │ │ ├── login.component.spec.ts
│ │ │ └── login.component.ts
│ │ └── register
│ │ │ ├── register.component.html
│ │ │ ├── register.component.scss
│ │ │ ├── register.component.spec.ts
│ │ │ └── register.component.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── browserslist
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── generated-models.ts
│ ├── index.html
│ ├── karma.conf.js
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── tsconfig.json
└── tslint.json
├── package.json
├── react-client
├── .gitignore
├── README.md
├── codegen.yml
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── AddPost.graphql
│ ├── AllPosts.graphql
│ ├── Home.tsx
│ ├── Login.tsx
│ ├── Router.tsx
│ ├── Signup.tsx
│ ├── components
│ │ └── FormError.tsx
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ └── utils
│ │ └── accounts.ts
└── tsconfig.json
├── renovate.json
├── server
├── .gitignore
├── .npmrc
├── codegen.yml
├── package.json
├── src
│ ├── index.ts
│ ├── modules
│ │ ├── app
│ │ │ └── app.module.ts
│ │ ├── db
│ │ │ └── db.module.ts
│ │ └── posts
│ │ │ ├── posts.module.ts
│ │ │ ├── providers
│ │ │ └── posts.provider.ts
│ │ │ ├── resolvers
│ │ │ ├── mutation.resolvers.ts
│ │ │ ├── post.resolvers.ts
│ │ │ ├── query.resolvers.ts
│ │ │ └── user.resolvers.ts
│ │ │ └── schema
│ │ │ ├── mutation.graphql
│ │ │ ├── post.graphql
│ │ │ ├── query.graphql
│ │ │ └── user.graphql
│ └── schema.ts
├── tsconfig.json
└── yarn.lock
└── stencil-client
├── .editorconfig
├── .gitignore
├── LICENSE
├── codegen.yml
├── package.json
├── readme.md
├── src
├── assets
│ └── icon
│ │ ├── favicon.ico
│ │ └── icon.png
├── components.d.ts
├── components
│ ├── app-home
│ │ ├── app-home.css
│ │ ├── app-home.e2e.ts
│ │ ├── app-home.spec.ts
│ │ └── app-home.tsx
│ ├── app-login
│ │ └── app-login.tsx
│ ├── app-root
│ │ ├── app-root.css
│ │ ├── app-root.e2e.ts
│ │ ├── app-root.spec.ts
│ │ └── app-root.tsx
│ ├── app-signup
│ │ └── app-signup.tsx
│ └── generated-models.tsx
├── global
│ ├── app.css
│ └── app.ts
├── graphql
│ ├── AddPost.graphql
│ └── AllPosts.graphql
├── helpers
│ └── accounts.ts
├── index.html
├── interfaces.d.ts
├── manifest.json
└── sw.js
├── stencil.config.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.log
3 |
--------------------------------------------------------------------------------
/angular-client/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/angular-client/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 | src/generated-models.ts
41 |
--------------------------------------------------------------------------------
/angular-client/README.md:
--------------------------------------------------------------------------------
1 | # AngularClient
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.0.6.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/angular-client/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-client": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {
12 | "@schematics/angular:component": {
13 | "styleext": "scss"
14 | }
15 | },
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "dist/angular-client",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": "src/polyfills.ts",
24 | "tsConfig": "src/tsconfig.app.json",
25 | "assets": [
26 | "src/favicon.ico",
27 | "src/assets"
28 | ],
29 | "styles": [
30 | "src/styles.scss"
31 | ],
32 | "scripts": []
33 | },
34 | "configurations": {
35 | "production": {
36 | "fileReplacements": [
37 | {
38 | "replace": "src/environments/environment.ts",
39 | "with": "src/environments/environment.prod.ts"
40 | }
41 | ],
42 | "optimization": true,
43 | "outputHashing": "all",
44 | "sourceMap": false,
45 | "extractCss": true,
46 | "namedChunks": false,
47 | "aot": true,
48 | "extractLicenses": true,
49 | "vendorChunk": false,
50 | "buildOptimizer": true,
51 | "budgets": [
52 | {
53 | "type": "initial",
54 | "maximumWarning": "2mb",
55 | "maximumError": "5mb"
56 | }
57 | ]
58 | }
59 | }
60 | },
61 | "serve": {
62 | "builder": "@angular-devkit/build-angular:dev-server",
63 | "options": {
64 | "browserTarget": "angular-client:build"
65 | },
66 | "configurations": {
67 | "production": {
68 | "browserTarget": "angular-client:build:production"
69 | }
70 | }
71 | },
72 | "extract-i18n": {
73 | "builder": "@angular-devkit/build-angular:extract-i18n",
74 | "options": {
75 | "browserTarget": "angular-client:build"
76 | }
77 | },
78 | "test": {
79 | "builder": "@angular-devkit/build-angular:karma",
80 | "options": {
81 | "main": "src/test.ts",
82 | "polyfills": "src/polyfills.ts",
83 | "tsConfig": "src/tsconfig.spec.json",
84 | "karmaConfig": "src/karma.conf.js",
85 | "styles": [
86 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
87 | "src/styles.scss"
88 | ],
89 | "scripts": [],
90 | "assets": [
91 | "src/favicon.ico",
92 | "src/assets"
93 | ]
94 | }
95 | },
96 | "lint": {
97 | "builder": "@angular-devkit/build-angular:tslint",
98 | "options": {
99 | "tsConfig": [
100 | "src/tsconfig.app.json",
101 | "src/tsconfig.spec.json"
102 | ],
103 | "exclude": [
104 | "**/node_modules/**"
105 | ]
106 | }
107 | }
108 | }
109 | },
110 | "angular-client-e2e": {
111 | "root": "e2e/",
112 | "projectType": "application",
113 | "prefix": "",
114 | "architect": {
115 | "e2e": {
116 | "builder": "@angular-devkit/build-angular:protractor",
117 | "options": {
118 | "protractorConfig": "e2e/protractor.conf.js",
119 | "devServerTarget": "angular-client:serve"
120 | },
121 | "configurations": {
122 | "production": {
123 | "devServerTarget": "angular-client:serve:production"
124 | }
125 | }
126 | },
127 | "lint": {
128 | "builder": "@angular-devkit/build-angular:tslint",
129 | "options": {
130 | "tsConfig": "e2e/tsconfig.e2e.json",
131 | "exclude": [
132 | "**/node_modules/**"
133 | ]
134 | }
135 | }
136 | }
137 | }
138 | },
139 | "defaultProject": "angular-client"
140 | }
141 |
--------------------------------------------------------------------------------
/angular-client/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: http://localhost:4000/graphql
3 | documents: ./src/**/*.graphql
4 | generates:
5 | ./src/generated-models.ts:
6 | plugins:
7 | - add: /* tslint:disable */
8 | - typescript-common
9 | - typescript-client
10 | - typescript-apollo-angular
11 |
--------------------------------------------------------------------------------
/angular-client/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './src/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: require('path').join(__dirname, './tsconfig.e2e.json')
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
--------------------------------------------------------------------------------
/angular-client/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('workspace-project App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getTitleText()).toEqual('Welcome to angular-client!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/angular-client/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getTitleText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/angular-client/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/angular-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-client",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "concurrently \"npm run generate:watch\" \"ng serve\"",
7 | "build": "npm run generate && ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e",
11 | "generate": "gql-gen --config ./codegen.yml",
12 | "generate:watch": "nodemon --exec yarn generate -e graphql"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@accounts/client": "^0.9.0",
17 | "@accounts/client-password": "^0.9.0",
18 | "@accounts/graphql-client": "^0.9.0",
19 | "@angular/animations": "~7.1.0",
20 | "@angular/cdk": "~7.1.0",
21 | "@angular/common": "~7.1.0",
22 | "@angular/compiler": "~7.1.0",
23 | "@angular/core": "~7.2.0",
24 | "@angular/forms": "^7.0.4",
25 | "@angular/http": "~7.2.0",
26 | "@angular/material": "^7.0.4",
27 | "@angular/platform-browser": "~7.2.0",
28 | "@angular/platform-browser-dynamic": "~7.2.0",
29 | "@angular/router": "~7.1.0",
30 | "apollo-angular": "^1.5.0",
31 | "apollo-angular-link-http": "^1.3.1",
32 | "apollo-cache-inmemory": "^1.3.2",
33 | "apollo-client": "^2.4.0",
34 | "apollo-link": "^1.2.0",
35 | "apollo-link-context": "^1.0.9",
36 | "core-js": "^2.5.4",
37 | "graphql": "^14.0.2",
38 | "graphql-tag": "^2.10.0",
39 | "hammerjs": "^2.0.8",
40 | "rxjs": "~6.3.3",
41 | "zone.js": "~0.8.26"
42 | },
43 | "devDependencies": {
44 | "@angular-devkit/build-angular": "~0.12.0",
45 | "@angular/cli": "~7.2.0",
46 | "@angular/compiler-cli": "~7.1.0",
47 | "@angular/language-service": "~7.2.0",
48 | "@types/jasmine": "~3.3.0",
49 | "@types/jasminewd2": "~2.0.3",
50 | "@types/node": "~10.12.0",
51 | "codelyzer": "~4.5.0",
52 | "concurrently": "^4.0.1",
53 | "graphql-code-generator": "^0.15.0",
54 | "graphql-codegen-add": "^0.15.0",
55 | "graphql-codegen-typescript-apollo-angular": "^0.15.0",
56 | "graphql-codegen-typescript-client": "^0.15.0",
57 | "graphql-codegen-typescript-common": "^0.15.0",
58 | "jasmine-core": "~3.3.0",
59 | "jasmine-spec-reporter": "~4.2.1",
60 | "karma": "~3.1.0",
61 | "karma-chrome-launcher": "~2.2.0",
62 | "karma-coverage-istanbul-reporter": "~2.0.1",
63 | "karma-jasmine": "~2.0.0",
64 | "karma-jasmine-html-reporter": "^1.0.0",
65 | "nodemon": "^1.18.6",
66 | "protractor": "~5.4.0",
67 | "ts-node": "~7.0.0",
68 | "tslint": "~5.11.0",
69 | "typescript": "~3.2.0"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/angular-client/src/app/accounts/accounts.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { GraphQLModule } from '../graphql/graphql.module';
4 | import { AccountsGraphQLClient } from '@accounts/graphql-client';
5 | import { Apollo } from 'apollo-angular';
6 | import { AccountsClient } from '@accounts/client';
7 | import { AccountsClientPassword } from '@accounts/client-password';
8 | import { CanActivateAuthService } from './can-activate-auth.service';
9 |
10 | function createGraphQLClient(apollo: Apollo) {
11 | return new AccountsGraphQLClient({
12 | graphQLClient: apollo.getClient(),
13 | });
14 | }
15 |
16 | function createAccountsClient(accountsGraphQL: AccountsGraphQLClient) {
17 | return new AccountsClient({}, accountsGraphQL);
18 | }
19 |
20 | function createAccountsPassword(accountsClient: AccountsClient) {
21 | return new AccountsClientPassword(accountsClient);
22 | }
23 |
24 | @NgModule({
25 | declarations: [],
26 | imports: [
27 | CommonModule,
28 | GraphQLModule
29 | ],
30 | providers: [
31 | {
32 | provide: AccountsGraphQLClient,
33 | useFactory: createGraphQLClient,
34 | deps: [Apollo]
35 | },
36 | {
37 | provide: AccountsClient,
38 | useFactory: createAccountsClient,
39 | deps: [AccountsGraphQLClient]
40 | },
41 | {
42 | provide: AccountsClientPassword,
43 | useFactory: createAccountsPassword,
44 | deps: [AccountsClient]
45 | },
46 | CanActivateAuthService,
47 | ]
48 | })
49 | export class AccountsModule { }
50 |
--------------------------------------------------------------------------------
/angular-client/src/app/accounts/can-activate-auth.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { CanActivateAuthService } from './can-activate-auth.service';
4 |
5 | describe('CanActivateAuthService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: CanActivateAuthService = TestBed.get(CanActivateAuthService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/angular-client/src/app/accounts/can-activate-auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
3 | import { AccountsClient } from '@accounts/client';
4 |
5 | @Injectable()
6 | export class CanActivateAuthService implements CanActivate {
7 |
8 | constructor(private accountsClient: AccountsClient, private router: Router) { }
9 | async canActivate(
10 | route: ActivatedRouteSnapshot,
11 | state: RouterStateSnapshot
12 | ) {
13 | const tokens = await this.accountsClient.refreshSession();
14 | if (!tokens) {
15 | this.router.navigateByUrl('/login');
16 | return false;
17 | }
18 | return true;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/angular-client/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { HomeComponent } from './home/home.component';
4 | import { LoginComponent } from './login/login.component';
5 | import { RegisterComponent } from './register/register.component';
6 | import { CanActivateAuthService } from './accounts/can-activate-auth.service';
7 |
8 | const routes: Routes = [
9 | {
10 | path: '',
11 | pathMatch: 'full',
12 | component: HomeComponent,
13 | canActivate: [CanActivateAuthService]
14 | },
15 | {
16 | path: 'login',
17 | component: LoginComponent
18 | },
19 | {
20 | path: 'register',
21 | component: RegisterComponent
22 | },
23 | ];
24 |
25 | @NgModule({
26 | imports: [RouterModule.forRoot(routes)],
27 | exports: [RouterModule]
28 | })
29 | export class AppRoutingModule { }
30 |
--------------------------------------------------------------------------------
/angular-client/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/angular-client/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/angular-client/src/app/app.component.scss
--------------------------------------------------------------------------------
/angular-client/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async(() => {
7 | TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | }));
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.debugElement.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'angular-client'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.debugElement.componentInstance;
26 | expect(app.title).toEqual('angular-client');
27 | });
28 |
29 | it('should render title in a h1 tag', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.debugElement.nativeElement;
33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-client!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/angular-client/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | title = 'angular-client';
10 | }
11 |
--------------------------------------------------------------------------------
/angular-client/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { FormsModule } from '@angular/forms';
3 | import { NgModule } from '@angular/core';
4 |
5 | import { AppRoutingModule } from './app-routing.module';
6 | import { AppComponent } from './app.component';
7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8 | import { GraphQLModule } from './graphql/graphql.module';
9 | import { HttpClientModule } from '@angular/common/http';
10 | import { AccountsModule } from './accounts/accounts.module';
11 | import { MatFormFieldModule, MatInputModule, MatButtonModule, MatCardModule, MatListModule, MatIconModule } from '@angular/material';
12 | import { HomeComponent } from './home/home.component';
13 | import { LoginComponent } from './login/login.component';
14 | import { RegisterComponent } from './register/register.component';
15 |
16 | @NgModule({
17 | declarations: [
18 | AppComponent,
19 | HomeComponent,
20 | LoginComponent,
21 | RegisterComponent,
22 | ],
23 | imports: [
24 | BrowserModule,
25 | FormsModule,
26 | AppRoutingModule,
27 | BrowserAnimationsModule,
28 | GraphQLModule,
29 | HttpClientModule,
30 | AccountsModule,
31 | MatListModule,
32 | MatIconModule,
33 | MatCardModule,
34 | MatFormFieldModule,
35 | MatInputModule,
36 | MatButtonModule,
37 | ],
38 | providers: [],
39 | bootstrap: [AppComponent]
40 | })
41 | export class AppModule { }
42 |
--------------------------------------------------------------------------------
/angular-client/src/app/graphql/graphql.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
3 | import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http';
4 | import {InMemoryCache} from 'apollo-cache-inmemory';
5 | import { setContext } from 'apollo-link-context';
6 |
7 | const auth = setContext(() => {
8 | // get the authentication token from local storage if it exists
9 | const token = localStorage.getItem('accounts:accessToken');
10 | // return the headers to the context so httpLink can read them
11 | // in this example we assume headers property exists
12 | // and it is an instance of HttpHeaders
13 | if (!token) {
14 | return {};
15 | } else {
16 | return {
17 | headers: {
18 | 'accounts-access-token': token
19 | }
20 | };
21 | }
22 | });
23 |
24 | const uri = 'http://localhost:4000/graphql'; // <-- add the URL of the GraphQL server here
25 | export function createApollo(httpLink: HttpLink) {
26 | return {
27 | link: auth.concat(httpLink.create({
28 | uri,
29 | })),
30 | cache: new InMemoryCache(),
31 | };
32 | }
33 |
34 | @NgModule({
35 | exports: [ApolloModule, HttpLinkModule],
36 | providers: [
37 | {
38 | provide: APOLLO_OPTIONS,
39 | useFactory: createApollo,
40 | deps: [HttpLink],
41 | },
42 | ],
43 | })
44 | export class GraphQLModule {}
45 |
--------------------------------------------------------------------------------
/angular-client/src/app/home/AddPost.graphql:
--------------------------------------------------------------------------------
1 | mutation AddPost($title: String, $content: String) {
2 | addPost(title: $title, content: $content) {
3 | id
4 | title
5 | content
6 | author {
7 | username
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/angular-client/src/app/home/AllPosts.graphql:
--------------------------------------------------------------------------------
1 | query AllPosts {
2 | allPosts{
3 | id
4 | title
5 | content
6 | author {
7 | username
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/angular-client/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 | Posts
26 |
27 | note
28 | {{post.title}}
29 | {{post.content}}
30 | Author: {{post.author.username}}
31 |
32 |
33 |
--------------------------------------------------------------------------------
/angular-client/src/app/home/home.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/angular-client/src/app/home/home.component.scss
--------------------------------------------------------------------------------
/angular-client/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-client/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { AllPosts, AddPost, AllPostsGQL, AddPostGQL } from 'src/generated-models';
3 | import { Observable } from 'rxjs';
4 | import { map } from 'rxjs/operators';
5 |
6 | @Component({
7 | selector: 'app-home',
8 | templateUrl: './home.component.html',
9 | styleUrls: ['./home.component.scss']
10 | })
11 | export class HomeComponent implements OnInit {
12 |
13 | constructor(private allPostsGql: AllPostsGQL, private addPostGql: AddPostGQL) { }
14 |
15 | data: Observable;
16 | error: string;
17 | newPost: AddPost.Variables = {
18 | title: '',
19 | content: ''
20 | };
21 |
22 | ngOnInit() {
23 | this.data = this.allPostsGql.fetch().pipe(
24 | map(results => results.data)
25 | );
26 | }
27 |
28 | async addPost() {
29 | try {
30 | await this.addPostGql.mutate({
31 | title: this.newPost.title,
32 | content: this.newPost.content
33 | }).toPromise();
34 | } catch (e) {
35 | this.error = e.message;
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/angular-client/src/app/login/login.component.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/angular-client/src/app/login/login.component.scss:
--------------------------------------------------------------------------------
1 | form {
2 | display: flex;
3 | flex-direction: column;
4 | mat-card {
5 | margin: auto;
6 | max-width: 400px;
7 | }
8 | * {
9 | width: 100%;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/angular-client/src/app/login/login.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { LoginComponent } from './login.component';
4 |
5 | describe('LoginComponent', () => {
6 | let component: LoginComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ LoginComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(LoginComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-client/src/app/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { AccountsClientPassword } from '@accounts/client-password';
3 | import { Router } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-login',
7 | templateUrl: './login.component.html',
8 | styleUrls: ['./login.component.scss']
9 | })
10 | export class LoginComponent implements OnInit {
11 |
12 | username: string;
13 | password: string;
14 | error: string;
15 |
16 | constructor(private accountsPassword: AccountsClientPassword, private router: Router) { }
17 |
18 | ngOnInit() {
19 | }
20 |
21 | async login() {
22 | try {
23 | await this.accountsPassword.login({
24 | password: this.password,
25 | user: {
26 | username: this.username,
27 | },
28 | });
29 | this.router.navigateByUrl('/');
30 | } catch (err) {
31 | this.error = err.message;
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/angular-client/src/app/register/register.component.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/angular-client/src/app/register/register.component.scss:
--------------------------------------------------------------------------------
1 | form {
2 | display: flex;
3 | flex-direction: column;
4 | mat-card {
5 | margin: auto;
6 | max-width: 400px;
7 | }
8 | * {
9 | width: 100%;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/angular-client/src/app/register/register.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RegisterComponent } from './register.component';
4 |
5 | describe('RegisterComponent', () => {
6 | let component: RegisterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ RegisterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RegisterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-client/src/app/register/register.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { AccountsClientPassword } from '@accounts/client-password';
3 | import { Router } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-register',
7 | templateUrl: './register.component.html',
8 | styleUrls: ['./register.component.scss']
9 | })
10 | export class RegisterComponent implements OnInit {
11 |
12 | username: string;
13 | password: string;
14 | error: string;
15 |
16 | constructor(private accountsPassword: AccountsClientPassword, private router: Router) { }
17 |
18 | ngOnInit() {
19 | }
20 |
21 | async register() {
22 | try {
23 | await this.accountsPassword.createUser({
24 | password: this.password,
25 | username: this.username,
26 | });
27 | this.router.navigateByUrl('/login');
28 | } catch (err) {
29 | this.error = err.message;
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/angular-client/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/angular-client/src/assets/.gitkeep
--------------------------------------------------------------------------------
/angular-client/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | #
5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
6 |
7 | > 0.5%
8 | last 2 versions
9 | Firefox ESR
10 | not dead
11 | not IE 9-11
--------------------------------------------------------------------------------
/angular-client/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/angular-client/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/angular-client/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/angular-client/src/favicon.ico
--------------------------------------------------------------------------------
/angular-client/src/generated-models.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 |
3 | export interface CreateUserInput {
4 | username?: string | null;
5 |
6 | email?: string | null;
7 |
8 | password?: string | null;
9 | }
10 |
11 | export interface TwoFactorSecretKeyInput {
12 | ascii?: string | null;
13 |
14 | base32?: string | null;
15 |
16 | hex?: string | null;
17 |
18 | qr_code_ascii?: string | null;
19 |
20 | qr_code_hex?: string | null;
21 |
22 | qr_code_base32?: string | null;
23 |
24 | google_auth_qr?: string | null;
25 |
26 | otpauth_url?: string | null;
27 | }
28 |
29 | export interface AuthenticateParamsInput {
30 | access_token?: string | null;
31 |
32 | access_token_secret?: string | null;
33 |
34 | provider?: string | null;
35 |
36 | password?: string | null;
37 |
38 | user?: UserInput | null;
39 |
40 | code?: string | null;
41 | }
42 |
43 | export interface UserInput {
44 | id?: string | null;
45 |
46 | email?: string | null;
47 |
48 | username?: string | null;
49 | }
50 |
51 | export interface AdditionalEntityFields {
52 | path?: string | null;
53 |
54 | type?: string | null;
55 | }
56 |
57 | // ====================================================
58 | // Documents
59 | // ====================================================
60 |
61 | export namespace AddPost {
62 | export type Variables = {
63 | title?: string | null;
64 | content?: string | null;
65 | };
66 |
67 | export type Mutation = {
68 | __typename?: "Mutation";
69 |
70 | addPost: AddPost | null;
71 | };
72 |
73 | export type AddPost = {
74 | __typename?: "Post";
75 |
76 | id: string | null;
77 |
78 | title: string | null;
79 |
80 | content: string | null;
81 |
82 | author: Author | null;
83 | };
84 |
85 | export type Author = {
86 | __typename?: "User";
87 |
88 | username: string | null;
89 | };
90 | }
91 |
92 | export namespace AllPosts {
93 | export type Variables = {};
94 |
95 | export type Query = {
96 | __typename?: "Query";
97 |
98 | allPosts: (AllPosts | null)[] | null;
99 | };
100 |
101 | export type AllPosts = {
102 | __typename?: "Post";
103 |
104 | id: string | null;
105 |
106 | title: string | null;
107 |
108 | content: string | null;
109 |
110 | author: Author | null;
111 | };
112 |
113 | export type Author = {
114 | __typename?: "User";
115 |
116 | username: string | null;
117 | };
118 | }
119 |
120 | // ====================================================
121 | // START: Apollo Angular template
122 | // ====================================================
123 |
124 | import { Injectable } from "@angular/core";
125 | import * as Apollo from "apollo-angular";
126 |
127 | import gql from "graphql-tag";
128 |
129 | // ====================================================
130 | // Apollo Services
131 | // ====================================================
132 |
133 | @Injectable({
134 | providedIn: "root"
135 | })
136 | export class AddPostGQL extends Apollo.Mutation<
137 | AddPost.Mutation,
138 | AddPost.Variables
139 | > {
140 | document: any = gql`
141 | mutation AddPost($title: String, $content: String) {
142 | addPost(title: $title, content: $content) {
143 | id
144 | title
145 | content
146 | author {
147 | username
148 | }
149 | }
150 | }
151 | `;
152 | }
153 | @Injectable({
154 | providedIn: "root"
155 | })
156 | export class AllPostsGQL extends Apollo.Query<
157 | AllPosts.Query,
158 | AllPosts.Variables
159 | > {
160 | document: any = gql`
161 | query AllPosts {
162 | allPosts {
163 | id
164 | title
165 | content
166 | author {
167 | username
168 | }
169 | }
170 | }
171 | `;
172 | }
173 |
174 | // ====================================================
175 | // END: Apollo Angular template
176 | // ====================================================
177 |
--------------------------------------------------------------------------------
/angular-client/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AngularClient
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/angular-client/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
--------------------------------------------------------------------------------
/angular-client/src/main.ts:
--------------------------------------------------------------------------------
1 | import 'hammerjs';
2 | import { enableProdMode } from '@angular/core';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 |
5 | import { AppModule } from './app/app.module';
6 | import { environment } from './environments/environment';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic().bootstrapModule(AppModule)
13 | .catch(err => console.error(err));
14 |
--------------------------------------------------------------------------------
/angular-client/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /**
38 | * If the application will be indexed by Google Search, the following is required.
39 | * Googlebot uses a renderer based on Chrome 41.
40 | * https://developers.google.com/search/docs/guides/rendering
41 | **/
42 | // import 'core-js/es6/array';
43 |
44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
45 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
46 |
47 | /** IE10 and IE11 requires the following for the Reflect API. */
48 | // import 'core-js/es6/reflect';
49 |
50 | /**
51 | * Web Animations `@angular/platform-browser/animations`
52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
54 | **/
55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
56 |
57 | /**
58 | * By default, zone.js will patch all possible macroTask and DomEvents
59 | * user can disable parts of macroTask/DomEvents patch by setting following flags
60 | */
61 |
62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
65 |
66 | /*
67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
69 | */
70 | // (window as any).__Zone_enable_cross_context_check = true;
71 |
72 | /***************************************************************************************************
73 | * Zone JS is required by default for Angular itself.
74 | */
75 | import 'zone.js/dist/zone'; // Included with Angular CLI.
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/angular-client/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import '~@angular/material/prebuilt-themes/indigo-pink.css';
3 | html, body { height: 100%; }
4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
5 |
--------------------------------------------------------------------------------
/angular-client/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/angular-client/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "exclude": [
8 | "test.ts",
9 | "**/*.spec.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/angular-client/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "test.ts",
12 | "polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/angular-client/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/angular-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "module": "es2015",
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "target": "es5",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2018",
18 | "dom",
19 | "esnext.asynciterable"
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/angular-client/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-redundant-jsdoc": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "no-output-on-prefix": true,
121 | "use-input-property-decorator": true,
122 | "use-output-property-decorator": true,
123 | "use-host-property-decorator": true,
124 | "no-input-rename": true,
125 | "no-output-rename": true,
126 | "use-life-cycle-interface": true,
127 | "use-pipe-transform-interface": true,
128 | "component-class-suffix": true,
129 | "directive-class-suffix": true
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "start:angular:dev": "cd angular-client && yarn start",
5 | "start:react:dev": "cd react-client && yarn start",
6 | "start:stencil:dev": "cd stencil-client && yarn start",
7 | "start:server:dev": "cd server && yarn dev"
8 | }
9 | }
--------------------------------------------------------------------------------
/react-client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | src/generated-models.tsx
--------------------------------------------------------------------------------
/react-client/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
--------------------------------------------------------------------------------
/react-client/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: http://localhost:4000/graphql
3 | documents: src/**/*.graphql
4 | generates:
5 | ./src/generated-models.tsx:
6 | plugins:
7 | - typescript-common
8 | - typescript-client
9 | - typescript-react-apollo
10 | config:
11 | noNamespaces: true
12 |
--------------------------------------------------------------------------------
/react-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@accounts/client": "^0.9.0",
7 | "@accounts/client-password": "^0.9.0",
8 | "@accounts/graphql-client": "^0.9.0",
9 | "@material-ui/core": "^3.5.1",
10 | "@types/graphql": "^14.0.3",
11 | "@types/jest": "23.3.10",
12 | "@types/node": "10.12.15",
13 | "@types/react": "16.7.17",
14 | "@types/react-dom": "16.0.11",
15 | "@types/react-router-dom": "^4.3.1",
16 | "apollo-boost": "^0.1.20",
17 | "concurrently": "4.1.0",
18 | "graphql": "^14.0.2",
19 | "graphql-code-generator": "0.15.0",
20 | "graphql-codegen-typescript-client": "0.15.0",
21 | "graphql-codegen-typescript-mongodb": "0.15.0",
22 | "graphql-codegen-typescript-react-apollo": "0.15.2",
23 | "graphql-tag": "^2.10.0",
24 | "nodemon": "^1.18.6",
25 | "react": "^16.6.3",
26 | "react-apollo": "^2.2.4",
27 | "react-dom": "^16.6.3",
28 | "react-router-dom": "^4.3.1",
29 | "react-scripts": "2.1.1",
30 | "typescript": "3.2.4"
31 | },
32 | "scripts": {
33 | "start": "concurrently \"yarn generate:watch\" \"react-scripts start\"",
34 | "build": "yarn generate && react-scripts build",
35 | "test": "react-scripts test",
36 | "eject": "react-scripts eject",
37 | "generate": "gql-gen --config ./codegen.yml",
38 | "generate:watch": "nodemon --exec yarn generate -e graphql"
39 | },
40 | "eslintConfig": {
41 | "extends": "react-app"
42 | },
43 | "browserslist": [
44 | ">0.2%",
45 | "not dead",
46 | "not ie <= 11",
47 | "not op_mini all"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/react-client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/react-client/public/favicon.ico
--------------------------------------------------------------------------------
/react-client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/react-client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/react-client/src/AddPost.graphql:
--------------------------------------------------------------------------------
1 | mutation AddPost($title: String, $content: String) {
2 | addPost(title: $title, content: $content) {
3 | id
4 | title
5 | content
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/react-client/src/AllPosts.graphql:
--------------------------------------------------------------------------------
1 | query AllPosts {
2 | allPosts{
3 | id
4 | title
5 | content
6 | author {
7 | username
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/react-client/src/Home.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Typography } from '@material-ui/core';
2 | import * as React from 'react';
3 | import { RouteComponentProps } from 'react-router-dom';
4 |
5 | import { accountsClient, accountsGraphQL } from './utils/accounts';
6 |
7 | import { AllPostsComponent, AddPostProps, AddPostComponent } from './generated-models';
8 |
9 | interface IState {
10 | user: any;
11 | newPost: AddPostProps
12 | }
13 |
14 | class Home extends React.Component, IState> {
15 | public state = {
16 | user: null as any,
17 | newPost: {
18 | title: '',
19 | content: ''
20 | }
21 | };
22 |
23 | public async componentDidMount() {
24 | // refresh the session to get a new accessToken if expired
25 | const tokens = await accountsClient.refreshSession();
26 | if (!tokens) {
27 | this.props.history.push('/login');
28 | return;
29 | }
30 | const user = await accountsGraphQL.getUser();
31 | await this.setState({ user });
32 | }
33 |
34 | public onLogout = async () => {
35 | await accountsClient.logout();
36 | this.props.history.push('/login');
37 | };
38 |
39 | public render() {
40 | const { user } = this.state;
41 | if (!user) {
42 | return null;
43 | }
44 |
45 | return (
46 |
47 |
You are logged in
48 |
Username: {user.username}
49 |
50 | {addPost => (
51 |
78 | )}
79 |
80 |
81 | Posts;
82 |
83 |
84 |
85 | {({data, loading, error}) => {
86 | if (loading) {
87 | return Loading...
88 | }
89 | if( error ) {
90 | return Error: {error}
91 | }
92 | if (data && data.allPosts ) {
93 | return data.allPosts.map((post, index) => (
94 | -
95 |
96 | {post.title}
97 |
98 |
99 | {post.content}
100 |
101 |
102 | {post.author && post.author.username}
103 |
104 |
105 | ))
106 | }
107 | }}
108 |
109 |
110 |
111 |
114 |
115 | );
116 | }
117 | }
118 |
119 | export default Home;
120 |
--------------------------------------------------------------------------------
/react-client/src/Login.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | FormControl,
4 | Input,
5 | InputLabel,
6 | Typography,
7 | withStyles,
8 | WithStyles,
9 | } from '@material-ui/core';
10 | import * as React from 'react';
11 | import { Link, RouteComponentProps } from 'react-router-dom';
12 |
13 | import FormError from './components/FormError';
14 | import { accountsPassword } from './utils/accounts';
15 |
16 | const styles = () => ({
17 | formContainer: {
18 | display: 'flex',
19 | flexDirection: 'column' as 'column',
20 | },
21 | });
22 |
23 | const SignUpLink = (props: any) => ;
24 |
25 | interface IState {
26 | username: string;
27 | password: string;
28 | code: string;
29 | error: string | null;
30 | }
31 |
32 | class Login extends React.Component & RouteComponentProps<{}>, IState> {
33 | public state = {
34 | code: '',
35 | username: '',
36 | error: null,
37 | password: '',
38 | };
39 |
40 | public onChangeUsername = ({ target }: React.ChangeEvent) => {
41 | this.setState({ username: target.value });
42 | };
43 |
44 | public onChangePassword = ({ target }: React.ChangeEvent) => {
45 | this.setState({ password: target.value });
46 | };
47 |
48 | public onSubmit = async (e: React.FormEvent) => {
49 | e.preventDefault();
50 | this.setState({ error: null });
51 | try {
52 | await accountsPassword.login({
53 | password: this.state.password,
54 | user: {
55 | username: this.state.username,
56 | },
57 | });
58 | this.props.history.push('/');
59 | } catch (err) {
60 | this.setState({ error: err.message });
61 | }
62 | };
63 |
64 | public render() {
65 | const { classes } = this.props;
66 | const { username, password, code, error } = this.state;
67 | return (
68 |
92 | );
93 | }
94 | }
95 |
96 | export default withStyles(styles)(Login);
97 |
--------------------------------------------------------------------------------
/react-client/src/Router.tsx:
--------------------------------------------------------------------------------
1 | import { CssBaseline, Grid, Paper, withStyles, WithStyles } from '@material-ui/core';
2 | import * as React from 'react';
3 | import { BrowserRouter, Route } from 'react-router-dom';
4 |
5 | import Home from './Home';
6 | import Login from './Login';
7 | import Signup from './Signup';
8 |
9 | const styles = () => ({
10 | container: {
11 | padding: 16,
12 | },
13 | root: {
14 | margin: 'auto',
15 | marginTop: 50,
16 | maxWidth: 500,
17 | },
18 | });
19 |
20 | const Router = ({ classes }: WithStyles<'root' | 'container'>) => {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default withStyles(styles)(Router);
39 |
40 |
--------------------------------------------------------------------------------
/react-client/src/Signup.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | FormControl,
4 | Input,
5 | InputLabel,
6 | Typography,
7 | withStyles,
8 | WithStyles,
9 | } from '@material-ui/core';
10 | import * as React from 'react';
11 | import { Link, RouteComponentProps } from 'react-router-dom';
12 |
13 | import FormError from './components/FormError';
14 | import { accountsPassword } from './utils/accounts';
15 |
16 | const styles = () => ({
17 | formContainer: {
18 | display: 'flex',
19 | flexDirection: 'column' as 'column',
20 | },
21 | });
22 |
23 | const LogInLink = (props: any) => ;
24 |
25 | interface IState {
26 | username: string;
27 | password: string;
28 | error: string | null;
29 | }
30 |
31 | class Signup extends React.Component<
32 | WithStyles<'formContainer'> & RouteComponentProps<{}>,
33 | IState
34 | > {
35 | public state = {
36 | username: '',
37 | error: null,
38 | password: '',
39 | };
40 |
41 | public onChangeEmail = ({ target }: React.ChangeEvent) => {
42 | this.setState({ username: target.value });
43 | };
44 |
45 | public onChangePassword = ({ target }: React.ChangeEvent) => {
46 | this.setState({ password: target.value });
47 | };
48 |
49 | public onSubmit = async (e: React.FormEvent) => {
50 | e.preventDefault();
51 | this.setState({ error: null });
52 | try {
53 | await accountsPassword.createUser({
54 | username: this.state.username,
55 | password: this.state.password
56 | });
57 | this.props.history.push('/login');
58 | } catch (err) {
59 | this.setState({ error: err.message });
60 | }
61 | };
62 |
63 | public render() {
64 | const { classes } = this.props;
65 | const { username, password, error } = this.state;
66 | return (
67 |
85 | );
86 | }
87 | }
88 |
89 | export default withStyles(styles)(Signup);
90 |
--------------------------------------------------------------------------------
/react-client/src/components/FormError.tsx:
--------------------------------------------------------------------------------
1 | import { Typography, withStyles, WithStyles } from '@material-ui/core';
2 | import * as React from 'react';
3 |
4 | const styles = () => ({
5 | formError: {
6 | color: 'red',
7 | },
8 | });
9 |
10 | interface IProps {
11 | error: string;
12 | }
13 |
14 | const FormError = ({ classes, error }: WithStyles<'formError'> & IProps) => {
15 | return {error};
16 | };
17 |
18 | export default withStyles(styles)(FormError);
19 |
--------------------------------------------------------------------------------
/react-client/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 | import Router from './Router';
4 | import { ApolloProvider } from 'react-apollo';
5 | import { apolloClient } from './utils/accounts';
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 | , document.getElementById('root') as HTMLElement);
12 |
--------------------------------------------------------------------------------
/react-client/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/react-client/src/utils/accounts.ts:
--------------------------------------------------------------------------------
1 | import { AccountsClient } from '@accounts/client';
2 | import { AccountsClientPassword } from '@accounts/client-password';
3 | import GraphQLClient from '@accounts/graphql-client';
4 | import ApolloClient from 'apollo-boost';
5 |
6 | const apolloClient = new ApolloClient({
7 | request: async operation => {
8 | const tokens = await accountsClient.getTokens();
9 | if (tokens) {
10 | operation.setContext({
11 | headers: {
12 | 'accounts-access-token': tokens.accessToken
13 | }
14 | });
15 | }
16 | },
17 | uri: 'http://localhost:4000/graphql',
18 | });
19 |
20 | const accountsGraphQL = new GraphQLClient({ graphQLClient: apolloClient });
21 | const accountsClient = new AccountsClient({}, accountsGraphQL);
22 | const accountsPassword = new AccountsClientPassword(accountsClient);
23 |
24 | export { accountsClient, accountsGraphQL, accountsPassword, apolloClient };
25 |
--------------------------------------------------------------------------------
/react-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "es2015",
6 | "esnext.asynciterable",
7 | "dom"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": false,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/renovate.json
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | src/generated-models.ts
3 |
--------------------------------------------------------------------------------
/server/.npmrc:
--------------------------------------------------------------------------------
1 | save-exact=true
--------------------------------------------------------------------------------
/server/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: ./src/schema.ts
3 | require:
4 | - ts-node/register/transpile-only
5 | - tsconfig-paths/register
6 | generates:
7 | ./src/generated-models.ts:
8 | plugins:
9 | - add: import { ModuleContext } from '@graphql-modules/core';
10 | - add: import { AccountsModuleContext } from '@accounts/graphql-api';
11 | - typescript-common
12 | - typescript-server
13 | - typescript-mongodb
14 | - typescript-resolvers
15 | config:
16 | contextType: ModuleContext
17 | mappers:
18 | Post: PostDbObject
19 |
20 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "graphql-modules-accountsjs-server",
3 | "version": "1.0.0",
4 | "description": "GraphQL-Modules AccountsJS Boilerplate",
5 | "main": "src/index.ts",
6 | "repository": "https://github.com/ardatan/graphql-modules-accountsjs-boilerplate",
7 | "author": "Arda TANRIKULU",
8 | "license": "MIT",
9 | "scripts": {
10 | "generate": "gql-gen",
11 | "generate:watch": "nodemon --exec yarn generate -e graphql",
12 | "start:server": "ts-node -r tsconfig-paths/register src/index.ts",
13 | "start:server:watch": "nodemon --exec yarn start:server -e ts",
14 | "dev": "concurrently \"yarn generate:watch\" \"yarn start:server:watch\"",
15 | "start": "yarn generate && yarn start:server",
16 | "build": "tsc"
17 | },
18 | "dependencies": {
19 | "@accounts/database-manager": "0.9.2",
20 | "@accounts/graphql-api": "0.9.0",
21 | "@accounts/mongo": "0.9.0",
22 | "@accounts/password": "0.9.2",
23 | "@accounts/server": "0.9.2",
24 | "@graphql-modules/core": "0.2.17",
25 | "@graphql-modules/epoxy": "0.2.17",
26 | "@graphql-modules/sonar": "0.2.17",
27 | "@types/mongodb": "3.1.17",
28 | "apollo-server": "2.3.1",
29 | "graphql": "14.1.1",
30 | "graphql-code-generator": "0.15.0",
31 | "graphql-codegen-add": "0.15.0",
32 | "graphql-codegen-typescript-common": "0.15.0",
33 | "graphql-codegen-typescript-mongodb": "0.15.0",
34 | "graphql-codegen-typescript-resolvers": "0.15.2",
35 | "graphql-codegen-typescript-server": "0.15.0",
36 | "graphql-tag": "2.10.1",
37 | "graphql-tools": "4.0.3",
38 | "mongodb": "3.1.10",
39 | "reflect-metadata": "0.1.13",
40 | "ts-node": "7.0.1",
41 | "tsconfig-paths": "3.7.0",
42 | "typescript": "3.2.4"
43 | },
44 | "devDependencies": {
45 | "concurrently": "4.1.0",
46 | "nodemon": "1.18.8"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/server/src/index.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import { ApolloServer } from 'apollo-server';
3 | import { MongoClient } from 'mongodb';
4 | import { AccountsServer } from '@accounts/server';
5 | import { AccountsPassword } from '@accounts/password';
6 | import AccountsMongoDB from '@accounts/mongo';
7 | import { AppModule } from '@modules/app/app.module';
8 |
9 | const PORT = process.env['PORT'] || 4000;
10 | const MONGO_URI = process.env['MONGO_URI'] || 'mongodb://localhost:27017/myDb';
11 | const TOKEN_SECRET = process.env['TOKEN_SECRET'] || 'myTokenSecret';
12 |
13 | async function main() {
14 | const mongoClient = await MongoClient.connect(MONGO_URI, {
15 | useNewUrlParser: true,
16 | native_parser: true
17 | });
18 | const db = mongoClient.db();
19 | // Create accounts server that holds a lower level of all accounts operations
20 | const accountsServer = new AccountsServer(
21 | {
22 | db: new AccountsMongoDB(db),
23 | tokenSecret: TOKEN_SECRET
24 | },
25 | {
26 | password: new AccountsPassword(),
27 | }
28 | );
29 | const { schema, context } = AppModule.forRoot({
30 | accountsServer,
31 | db
32 | });
33 | const apolloServer = new ApolloServer({
34 | schema,
35 | context,
36 | introspection: true
37 | });
38 | const { url } = await apolloServer.listen(PORT);
39 | console.log(`Server listening: ${url}`);
40 | }
41 |
42 | main();
--------------------------------------------------------------------------------
/server/src/modules/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { GraphQLModule } from '@graphql-modules/core';
2 | import { AccountsModule } from '@accounts/graphql-api';
3 | import AccountsServer from '@accounts/server';
4 | import { Db } from 'mongodb';
5 | import { PostsModule } from '@modules/posts/posts.module';
6 | import { DbModule } from '@modules/db/db.module';
7 |
8 | export interface AppModuleConfig {
9 | accountsServer: AccountsServer;
10 | db: Db;
11 | }
12 |
13 | export const AppModule = new GraphQLModule({
14 | name: 'app',
15 | imports: ({ config: { accountsServer, db } }) => [
16 | AccountsModule.forRoot({ accountsServer }),
17 | DbModule.forRoot({ db }),
18 | PostsModule,
19 | ],
20 | configRequired: true,
21 | });
22 |
--------------------------------------------------------------------------------
/server/src/modules/db/db.module.ts:
--------------------------------------------------------------------------------
1 | import { GraphQLModule } from "@graphql-modules/core";
2 | import { DIRECTIVES } from 'graphql-codegen-typescript-mongodb';
3 | import { Db } from "mongodb";
4 |
5 | export interface DbModuleConfig {
6 | db: Db;
7 | }
8 |
9 | export const DbModule = new GraphQLModule({
10 | name: 'db',
11 | typeDefs: DIRECTIVES,
12 | providers: ({ config: { db }}) => [
13 | {
14 | provide: Db,
15 | useValue: db,
16 | }
17 | ],
18 | directiveResolvers: {
19 | id: (_, { _id }) => _id.toString(),
20 | },
21 | configRequired: true,
22 | });
23 |
--------------------------------------------------------------------------------
/server/src/modules/posts/posts.module.ts:
--------------------------------------------------------------------------------
1 | import { GraphQLModule } from "@graphql-modules/core";
2 | import { loadResolversFiles, loadSchemaFiles } from '@graphql-modules/sonar';
3 | import { mergeResolvers } from '@graphql-modules/epoxy';
4 | import { AccountsModule } from "@accounts/graphql-api";
5 | import { PostsProvider } from './providers/posts.provider';
6 | import { DbModule } from "@modules/db/db.module";
7 |
8 | export const PostsModule = new GraphQLModule({
9 | name: 'posts',
10 | typeDefs: loadSchemaFiles(__dirname + '/schema/'),
11 | resolvers: mergeResolvers(loadResolversFiles(__dirname + '/resolvers/')),
12 | imports: [
13 | AccountsModule.forChild(),
14 | DbModule.forChild()
15 | ],
16 | providers: [
17 | PostsProvider
18 | ]
19 | });
20 |
--------------------------------------------------------------------------------
/server/src/modules/posts/providers/posts.provider.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@graphql-modules/di";
2 | import { Db, Collection } from "mongodb";
3 | import { PostDbObject } from "@models";
4 |
5 | @Injectable()
6 | export class PostsProvider {
7 | collection: Collection;
8 | constructor(db: Db) {
9 | this.collection = db.collection('posts');
10 | }
11 | getAllPosts() {
12 | return this.collection.find().toArray();
13 | }
14 | getPostsOfUser(userId: string) {
15 | return this.collection.find({ userId }).toArray();
16 | }
17 | async addPost(title, content, userId): Promise {
18 | const { insertedId } = await this.collection.insertOne({
19 | title,
20 | content,
21 | userId,
22 | } as PostDbObject);
23 | return {
24 | _id: insertedId,
25 | title,
26 | content,
27 | userId
28 | };
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/server/src/modules/posts/resolvers/mutation.resolvers.ts:
--------------------------------------------------------------------------------
1 | import { IResolvers } from "@models";
2 | import { PostsProvider } from "../providers/posts.provider";
3 | import { PostsModule } from "../posts.module";
4 |
5 | export default ({ injector }: typeof PostsModule): IResolvers => ({
6 | Mutation: {
7 | addPost: (_, { title, content }, { userId }) => injector.get(PostsProvider).addPost(title, content, userId),
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/server/src/modules/posts/resolvers/post.resolvers.ts:
--------------------------------------------------------------------------------
1 | import { PostsModule } from "../posts.module";
2 | import { IResolvers } from '@models';
3 | import { AccountsServer } from "@accounts/server";
4 |
5 | export default ({ injector }: typeof PostsModule): IResolvers => ({
6 | Post: {
7 | author: ({ userId }) => injector.get(AccountsServer).findUserById(userId)
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/server/src/modules/posts/resolvers/query.resolvers.ts:
--------------------------------------------------------------------------------
1 | import { IResolvers } from "@models";
2 | import { PostsModule } from "../posts.module";
3 | import { PostsProvider } from "../providers/posts.provider";
4 |
5 | export default ({ injector }: typeof PostsModule): IResolvers => ({
6 | Query: {
7 | allPosts: () => injector.get(PostsProvider).getAllPosts()
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/server/src/modules/posts/resolvers/user.resolvers.ts:
--------------------------------------------------------------------------------
1 | import { PostsProvider } from "../providers/posts.provider";
2 | import { IResolvers } from "@models";
3 | import { PostsModule } from "../posts.module";
4 |
5 | export default ({ injector }: typeof PostsModule): IResolvers => ({
6 | User: {
7 | posts: ({ _id }: any) => injector.get(PostsProvider).getPostsOfUser(_id.toString()),
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/server/src/modules/posts/schema/mutation.graphql:
--------------------------------------------------------------------------------
1 | type Mutation {
2 | addPost(title: String, content: String): Post @auth
3 | }
4 |
--------------------------------------------------------------------------------
/server/src/modules/posts/schema/post.graphql:
--------------------------------------------------------------------------------
1 | type Post @entity {
2 | id: ID @id
3 | title: String @column
4 | content: String @column
5 | author: User @column(overrideType: "string") @map(path: "userId")
6 | }
7 |
--------------------------------------------------------------------------------
/server/src/modules/posts/schema/query.graphql:
--------------------------------------------------------------------------------
1 | type Query {
2 | allPosts: [Post]
3 | }
4 |
--------------------------------------------------------------------------------
/server/src/modules/posts/schema/user.graphql:
--------------------------------------------------------------------------------
1 | extend type User {
2 | posts: [Post]
3 | }
4 |
--------------------------------------------------------------------------------
/server/src/schema.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import { AppModule } from '@modules/app/app.module';
3 | import { AccountsServer } from '@accounts/server';
4 | import { Db } from 'mongodb';
5 | const AccountsServerMock = { getServices: () => ({ password: {} }) } as any as AccountsServer;
6 | const DbMock = { collection: () => ({}) } as any as Db;
7 | export default AppModule.forRoot({ accountsServer: AccountsServerMock, db: DbMock }).typeDefs;
8 |
--------------------------------------------------------------------------------
/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["es2017", "esnext.asynciterable", "dom"],
4 | "target": "es2017",
5 | "module": "commonjs",
6 | "moduleResolution": "node",
7 | "sourceMap": true,
8 | "experimentalDecorators": true,
9 | "emitDecoratorMetadata": true,
10 | "outDir": "dist",
11 | "typeRoots": ["node_modules/@types"],
12 | "paths": {
13 | "@models": ["./generated-models"],
14 | "@modules/*": ["./modules/*"]
15 | },
16 | "baseUrl": "./src/",
17 | "allowSyntheticDefaultImports": true
18 | },
19 | "include": ["src/**/*"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/stencil-client/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/stencil-client/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | !www/favicon.ico
3 | www/
4 |
5 | *~
6 | *.sw[mnpcod]
7 | *.log
8 | *.lock
9 | !yarn.lock
10 | *.tmp
11 | *.tmp.*
12 | log.txt
13 | *.sublime-project
14 | *.sublime-workspace
15 |
16 | .stencil/
17 | .idea/
18 | .vscode/
19 | .sass-cache/
20 | .versions/
21 | node_modules/
22 | $RECYCLE.BIN/
23 |
24 | .DS_Store
25 | Thumbs.db
26 | UserInterfaceState.xcuserstate
27 | .env
28 |
--------------------------------------------------------------------------------
/stencil-client/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 stencil-client
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/stencil-client/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: http://localhost:4000/graphql
3 | documents: src/**/*.graphql
4 | generates:
5 | ./src/components/generated-models.tsx:
6 | plugins:
7 | - typescript-common
8 | - typescript-client
9 | - typescript-stencil-apollo
10 | config:
11 | noNamespaces: true
12 |
--------------------------------------------------------------------------------
/stencil-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stencil-client",
3 | "private": true,
4 | "version": "0.0.1",
5 | "description": "stencil-client",
6 | "license": "MIT",
7 | "files": [
8 | "dist/"
9 | ],
10 | "scripts": {
11 | "build": "stencil build",
12 | "start": "stencil build --dev --watch --serve",
13 | "test": "stencil test --spec --e2e",
14 | "test.watch": "stencil test --spec --e2e --watch"
15 | },
16 | "dependencies": {
17 | "@accounts/client": "^0.9.0",
18 | "@accounts/client-password": "^0.9.0",
19 | "@accounts/graphql-client": "^0.9.0",
20 | "@ionic/core": "4.0.0-rc.2",
21 | "apollo-boost": "^0.1.23",
22 | "graphql": "^14.0.2",
23 | "stencil-apollo": "^0.0.1"
24 | },
25 | "devDependencies": {
26 | "@stencil/core": "0.17.0",
27 | "@types/graphql": "^14.0.3",
28 | "graphql-code-generator": "^0.15.2",
29 | "graphql-codegen-typescript-client": "^0.15.2",
30 | "graphql-codegen-typescript-common": "^0.15.2",
31 | "graphql-codegen-typescript-stencil-apollo": "^0.14.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/stencil-client/readme.md:
--------------------------------------------------------------------------------
1 | # Ionic PWA Toolkit Beta
2 |
3 | The PWA Toolkit is a starting point for building Progressive Web Apps using Ionic and Stencil.
4 | This combination of tools gives you the ability to build a fast, efficient PWA out of the box.
5 |
6 | Note: This project is **Beta** and uses a beta release of `@ionic/core`.
7 |
8 | For more info check out our [homepage](https://ionicframework.com/pwa/toolkit)!
9 |
10 | ## Features
11 |
12 | * `@ionic/core` for the UI.
13 | * Stencil for the application logic and routing
14 | * Push Notifications setup
15 | * Unit Tests
16 | * Pre-rendering
17 | * Lazy-loading and code splitting
18 | * Intelligent Polyfills
19 | * Modern mode: ES6/ESM for new browser, ES5 for older
20 | * Service Worker, App manifest, iOS meta tags
21 | * Theming using CSS variables
22 |
23 | ## Getting Started
24 |
25 | To start building, clone this repo to a new directory:
26 |
27 | ```bash
28 | npm init stencil ionic-pwa
29 | ```
30 |
31 |
32 | ## Production
33 |
34 | To build for production, run:
35 |
36 | ```bash
37 | npm run build
38 | ```
39 |
40 | A production build includes:
41 |
42 | * Minified code bundles
43 | * Generated Service workers
44 | * App manifest
45 |
46 | ## Hosting
47 |
48 | Apps should be hosted on through HTTPS, and if possible, through a provider that supports HTTP2.
49 | One provider that does support this is [Firebase Hosting](https://firebase.google.com/docs/hosting/).
50 |
51 | ## H2 Push
52 |
53 | We recommend setting up HTTP2 Push on Firebase. H2 Push may sound complicated, but it's actually a simple concept. To learn about it, take a look at this [article](https://en.wikipedia.org/wiki/HTTP/2_Server_Push).
54 |
55 | To set this up for `my-app`:
56 |
57 | * Do a production build of the app: `npm run build`
58 | * Serve your WWW folder locally using a local http server and open in your browser.
59 | * https://www.npmjs.com/package/http-server works pretty well for this. You can serve your www folder by running `http-server www`.
60 | * Open the DevTools and look at the network tab.
61 | * Reload the page and you should see all of your files show up in the network tab. Excluding the `sw.js` file, these are the files you want to H2 push.
62 | * List these files in the link headers of your firebase.json file. For a syntax reference, review this [article](https://w3c.github.io/preload/#server-push-http-2)
63 |
64 | ## Service Workers
65 |
66 | Service workers are generated via the Stencil build tool. For more information on how they can be configured, see the [Service Worker docs](https://stenciljs.com/docs/service-workers).
67 |
68 | ## Developing with a Service Worker
69 |
70 | For most cases, you'll want to develop your app without generating a Service Worker. But if you'd like to test out Web Push Notifications or Background Sync, you'll need to have one generated. To generate a Service Worker during dev builds, we've added the npm script:
71 |
72 | ```
73 | npm run start.sw
74 | ```
75 |
76 | This will start a dev build and generate a Service Worker as well.
77 |
78 | ## Unit Tests
79 |
80 | To run the unit tests once, run:
81 |
82 | ```bash
83 | npm test
84 | ```
85 |
86 | To run the unit tests and watch for file changes during development, run:
87 |
88 | ```bash
89 | npm run test.watch
90 | ```
91 |
92 | ## Testing your PWA's performance
93 |
94 | We recommend using https://www.webpagetest.org/easy with the `Run Lighthouse Audit` option turned on.
95 | This will give you an in depth look into your app's load performance on the average device connected to the average network.
96 | For more info on how to use webpagetest check out [this article](https://zoompf.com/blog/2015/07/the-seo-experts-guide-to-web-performance-using-webpagetest-2)
97 |
--------------------------------------------------------------------------------
/stencil-client/src/assets/icon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/stencil-client/src/assets/icon/favicon.ico
--------------------------------------------------------------------------------
/stencil-client/src/assets/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/stencil-client/src/assets/icon/icon.png
--------------------------------------------------------------------------------
/stencil-client/src/components.d.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /**
3 | * This is an autogenerated file created by the Stencil compiler.
4 | * It contains typing information for all components that exist in this project.
5 | */
6 |
7 |
8 | import '@stencil/core';
9 |
10 | import '@ionic/core';
11 | import 'ionicons';
12 | import 'stencil-apollo';
13 |
14 |
15 | export namespace Components {
16 |
17 | interface AppHome {}
18 | interface AppHomeAttributes extends StencilHTMLAttributes {}
19 |
20 | interface AppLogin {}
21 | interface AppLoginAttributes extends StencilHTMLAttributes {}
22 |
23 | interface AppRoot {}
24 | interface AppRootAttributes extends StencilHTMLAttributes {}
25 |
26 | interface AppSignup {}
27 | interface AppSignupAttributes extends StencilHTMLAttributes {}
28 | }
29 |
30 | declare global {
31 | interface StencilElementInterfaces {
32 | 'AppHome': Components.AppHome;
33 | 'AppLogin': Components.AppLogin;
34 | 'AppRoot': Components.AppRoot;
35 | 'AppSignup': Components.AppSignup;
36 | }
37 |
38 | interface StencilIntrinsicElements {
39 | 'app-home': Components.AppHomeAttributes;
40 | 'app-login': Components.AppLoginAttributes;
41 | 'app-root': Components.AppRootAttributes;
42 | 'app-signup': Components.AppSignupAttributes;
43 | }
44 |
45 |
46 | interface HTMLAppHomeElement extends Components.AppHome, HTMLStencilElement {}
47 | var HTMLAppHomeElement: {
48 | prototype: HTMLAppHomeElement;
49 | new (): HTMLAppHomeElement;
50 | };
51 |
52 | interface HTMLAppLoginElement extends Components.AppLogin, HTMLStencilElement {}
53 | var HTMLAppLoginElement: {
54 | prototype: HTMLAppLoginElement;
55 | new (): HTMLAppLoginElement;
56 | };
57 |
58 | interface HTMLAppRootElement extends Components.AppRoot, HTMLStencilElement {}
59 | var HTMLAppRootElement: {
60 | prototype: HTMLAppRootElement;
61 | new (): HTMLAppRootElement;
62 | };
63 |
64 | interface HTMLAppSignupElement extends Components.AppSignup, HTMLStencilElement {}
65 | var HTMLAppSignupElement: {
66 | prototype: HTMLAppSignupElement;
67 | new (): HTMLAppSignupElement;
68 | };
69 |
70 | interface HTMLElementTagNameMap {
71 | 'app-home': HTMLAppHomeElement
72 | 'app-login': HTMLAppLoginElement
73 | 'app-root': HTMLAppRootElement
74 | 'app-signup': HTMLAppSignupElement
75 | }
76 |
77 | interface ElementTagNameMap {
78 | 'app-home': HTMLAppHomeElement;
79 | 'app-login': HTMLAppLoginElement;
80 | 'app-root': HTMLAppRootElement;
81 | 'app-signup': HTMLAppSignupElement;
82 | }
83 |
84 |
85 | export namespace JSX {
86 | export interface Element {}
87 | export interface IntrinsicElements extends StencilIntrinsicElements {
88 | [tagName: string]: any;
89 | }
90 | }
91 | export interface HTMLAttributes extends StencilHTMLAttributes {}
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-home/app-home.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/stencil-client/src/components/app-home/app-home.css
--------------------------------------------------------------------------------
/stencil-client/src/components/app-home/app-home.e2e.ts:
--------------------------------------------------------------------------------
1 | import { newE2EPage } from '@stencil/core/testing';
2 |
3 | describe('app-home', () => {
4 | it('renders', async () => {
5 | const page = await newE2EPage();
6 | await page.setContent('');
7 |
8 | const element = await page.find('app-home');
9 | expect(element).toHaveClass('hydrated');
10 | });
11 |
12 | it('contains a "Profile Page" button', async () => {
13 | const page = await newE2EPage();
14 | await page.setContent('');
15 |
16 | const element = await page.find('app-home ion-content ion-button');
17 | expect(element.textContent).toEqual('Profile page');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-home/app-home.spec.ts:
--------------------------------------------------------------------------------
1 | import { AppHome } from './app-home';
2 |
3 | describe('app', () => {
4 | it('builds', () => {
5 | expect(new AppHome()).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-home/app-home.tsx:
--------------------------------------------------------------------------------
1 | import { Component, State, ComponentDidLoad } from '@stencil/core';
2 | import { AllPostsComponent, AddPostComponent } from '../generated-models';
3 | import { accountsClient, accountsGraphQL } from '../../helpers/accounts';
4 |
5 | @Component({
6 | tag: 'app-home',
7 | styleUrl: 'app-home.css'
8 | })
9 | export class AppHome implements ComponentDidLoad {
10 |
11 | @State() user: any = null;
12 | @State() title: string;
13 | @State() content: string;
14 |
15 | public async componentDidLoad() {
16 | // refresh the session to get a new accessToken if expired
17 | const tokens = await accountsClient.refreshSession();
18 | if (!tokens) {
19 | const ionRouter = document.querySelector('ion-router');
20 | ionRouter.push('/login');
21 | return;
22 | }
23 | this.user = await accountsGraphQL.getUser();
24 | }
25 |
26 | public onLogout = async () => {
27 | await accountsClient.logout();
28 | const ionRouter = document.querySelector('ion-router');
29 | ionRouter.push('/login');
30 | }
31 |
32 | render() {
33 | if (!this.user) {
34 | return null;
35 | }
36 | return [
37 |
38 |
39 | Home
40 |
41 | ,
42 |
43 |
44 |
45 | You are logged in.
46 |
47 |
48 | Username: {this.user.username}
49 |
50 | (
52 |
82 | )
83 | } />
84 |
85 | Posts;
86 |
87 |
114 | Logout
115 |
116 | ];
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-login/app-login.tsx:
--------------------------------------------------------------------------------
1 | import { Component, State } from "@stencil/core";
2 | import { accountsPassword } from "../../helpers/accounts";
3 |
4 | @Component({
5 | tag: 'app-login'
6 | })
7 | export class AppLogin {
8 | @State() username: string;
9 | @State() password: string;
10 |
11 | async login() {
12 | try {
13 | await accountsPassword.login({
14 | password: this.password,
15 | user: {
16 | username: this.username,
17 | },
18 | });
19 | const ionRouter = document.querySelector('ion-router');
20 | return ionRouter.push('/');
21 | } catch (err) {
22 | alert(err.message);
23 | }
24 | }
25 |
26 | goSignup(){
27 | const ionRouter = document.querySelector('ion-router');
28 | return ionRouter.push('/signup');
29 | }
30 |
31 | render() {
32 | return [
33 |
34 |
35 | Login
36 |
37 | ,
38 |
39 |
40 |
41 | this.username = target['value'] } />
42 |
43 |
44 | this.password = target['value'] } />
45 |
46 |
47 | this.login() }>Login
48 |
49 |
50 | this.goSignup() }>Signup
51 |
52 |
53 |
54 | ]
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-root/app-root.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/stencil-client/src/components/app-root/app-root.css
--------------------------------------------------------------------------------
/stencil-client/src/components/app-root/app-root.e2e.ts:
--------------------------------------------------------------------------------
1 | import { newE2EPage } from '@stencil/core/testing';
2 |
3 | describe('app-root', () => {
4 | it('renders', async () => {
5 | const page = await newE2EPage({ url: '/'});
6 |
7 | const element = await page.find('app-root');
8 | expect(element).toHaveClass('hydrated');
9 | });
10 |
11 | it('renders an ion-app', async () => {
12 | const page = await newE2EPage({ url: '/'});
13 |
14 | const element = await page.find('app-root > ion-app');
15 | expect(element).toHaveClass('hydrated');
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-root/app-root.spec.ts:
--------------------------------------------------------------------------------
1 | import { AppRoot } from './app-root';
2 |
3 | describe('app-root', () => {
4 | it('builds', () => {
5 | expect(new AppRoot()).toBeTruthy();
6 | });
7 |
8 | describe('onSWUpdate', () => {
9 | let appRoot: AppRoot;
10 | let mockToast;
11 | beforeEach(() => {
12 | appRoot = new AppRoot();
13 | mockToast = {
14 | present: jest.fn().mockReturnValue(Promise.resolve()),
15 | onWillDismiss: jest.fn().mockReturnValue(Promise.resolve())
16 | };
17 | appRoot.toastCtrl = {
18 | create: jest.fn().mockReturnValue(Promise.resolve(mockToast))
19 | } as any;
20 | window.location.reload = jest.fn();
21 | });
22 |
23 | it('creates a new toast', () => {
24 | appRoot.onSWUpdate();
25 | expect(appRoot.toastCtrl.create).toHaveBeenCalledTimes(1);
26 | expect(appRoot.toastCtrl.create).toHaveBeenCalledWith({
27 | message: 'New version available',
28 | showCloseButton: true,
29 | closeButtonText: 'Reload'
30 | });
31 | });
32 |
33 | it('presents the toast', async () => {
34 | await appRoot.onSWUpdate();
35 | expect(mockToast.present).toHaveReturnedTimes(1);
36 | });
37 |
38 | it('reloads the app', async () => {
39 | await appRoot.onSWUpdate();
40 | expect(window.location.reload).toHaveReturnedTimes(1);
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-root/app-root.tsx:
--------------------------------------------------------------------------------
1 | import { Component, Prop, Listen } from '@stencil/core';
2 | import { apolloClient } from '../../helpers/accounts';
3 |
4 | @Component({
5 | tag: 'app-root',
6 | styleUrl: 'app-root.css'
7 | })
8 | export class AppRoot {
9 |
10 | @Prop({ connect: 'ion-toast-controller' }) toastCtrl: HTMLIonToastControllerElement;
11 |
12 | /**
13 | * Handle service worker updates correctly.
14 | * This code will show a toast letting the
15 | * user of the PWA know that there is a
16 | * new version available. When they click the
17 | * reload button it then reloads the page
18 | * so that the new service worker can take over
19 | * and serve the fresh content
20 | */
21 | @Listen('window:swUpdate')
22 | async onSWUpdate() {
23 | const toast = await this.toastCtrl.create({
24 | message: 'New version available',
25 | showCloseButton: true,
26 | closeButtonText: 'Reload'
27 | });
28 | await toast.present();
29 | await toast.onWillDismiss();
30 | window.location.reload();
31 | }
32 |
33 | render() {
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/stencil-client/src/components/app-signup/app-signup.tsx:
--------------------------------------------------------------------------------
1 | import { Component, State } from "@stencil/core";
2 | import { accountsPassword } from "../../helpers/accounts";
3 |
4 | @Component({
5 | tag: 'app-signup'
6 | })
7 | export class AppSignup {
8 | @State() username: string;
9 | @State() password: string;
10 |
11 | async signup() {
12 | try {
13 | await accountsPassword.createUser({
14 | username: this.username,
15 | password: this.password
16 | });
17 | const ionRouter = document.querySelector('ion-router');
18 | return ionRouter.push('/login');
19 | } catch (err) {
20 | alert(err.message);
21 | }
22 | }
23 |
24 | goLogin(){
25 | const ionRouter = document.querySelector('ion-router');
26 | return ionRouter.push('/login');
27 | }
28 |
29 | render() {
30 | return [
31 |
32 |
33 | Signup
34 |
35 | ,
36 |
37 |
38 |
39 | this.username = target['value'] } />
40 |
41 |
42 | this.password = target['value'] } />
43 |
44 |
45 | this.signup() }>Signup
46 |
47 |
48 | this.goLogin() }>Login
49 |
50 |
51 |
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/stencil-client/src/components/generated-models.tsx:
--------------------------------------------------------------------------------
1 | export type Maybe = T | null;
2 |
3 | export interface CreateUserInput {
4 | username?: Maybe;
5 |
6 | email?: Maybe;
7 |
8 | password?: Maybe;
9 | }
10 |
11 | export interface TwoFactorSecretKeyInput {
12 | ascii?: Maybe;
13 |
14 | base32?: Maybe;
15 |
16 | hex?: Maybe;
17 |
18 | qr_code_ascii?: Maybe;
19 |
20 | qr_code_hex?: Maybe;
21 |
22 | qr_code_base32?: Maybe;
23 |
24 | google_auth_qr?: Maybe;
25 |
26 | otpauth_url?: Maybe;
27 | }
28 |
29 | export interface AuthenticateParamsInput {
30 | access_token?: Maybe;
31 |
32 | access_token_secret?: Maybe;
33 |
34 | provider?: Maybe;
35 |
36 | password?: Maybe;
37 |
38 | user?: Maybe;
39 |
40 | code?: Maybe;
41 | }
42 |
43 | export interface UserInput {
44 | id?: Maybe;
45 |
46 | email?: Maybe;
47 |
48 | username?: Maybe;
49 | }
50 |
51 | export interface AdditionalEntityFields {
52 | path?: Maybe;
53 |
54 | type?: Maybe;
55 | }
56 |
57 | // ====================================================
58 | // Documents
59 | // ====================================================
60 |
61 | export type AddPostVariables = {
62 | title?: Maybe;
63 | content?: Maybe;
64 | };
65 |
66 | export type AddPostMutation = {
67 | __typename?: "Mutation";
68 |
69 | addPost: Maybe;
70 | };
71 |
72 | export type AddPostAddPost = {
73 | __typename?: "Post";
74 |
75 | id: Maybe;
76 |
77 | title: Maybe;
78 |
79 | content: Maybe;
80 | };
81 |
82 | export type AllPostsVariables = {};
83 |
84 | export type AllPostsQuery = {
85 | __typename?: "Query";
86 |
87 | allPosts: Maybe;
88 | };
89 |
90 | export type AllPostsAllPosts = {
91 | __typename?: "Post";
92 |
93 | id: Maybe;
94 |
95 | title: Maybe;
96 |
97 | content: Maybe;
98 |
99 | author: Maybe;
100 | };
101 |
102 | export type AllPostsAuthor = {
103 | __typename?: "User";
104 |
105 | username: Maybe;
106 | };
107 |
108 | import { FunctionalComponent } from "@stencil/core";
109 |
110 | import gql from "graphql-tag";
111 |
112 | // ====================================================
113 | // Components
114 | // ====================================================
115 |
116 | export const AddPostDocument = gql`
117 | mutation AddPost($title: String, $content: String) {
118 | addPost(title: $title, content: $content) {
119 | id
120 | title
121 | content
122 | }
123 | }
124 | `;
125 | export interface AddPostComponentProps {
126 | variables?: AddPostVariables;
127 | onReady?: import("stencil-apollo/dist/types/components/apollo-mutation/types").OnMutationReadyFn<
128 | AddPostMutation,
129 | AddPostVariables
130 | >;
131 | }
132 | export const AddPostComponent: FunctionalComponent = (
133 | props,
134 | children
135 | ) => {
136 | return (
137 |
138 | {children}
139 |
140 | );
141 | };
142 | export const AllPostsDocument = gql`
143 | query AllPosts {
144 | allPosts {
145 | id
146 | title
147 | content
148 | author {
149 | username
150 | }
151 | }
152 | }
153 | `;
154 | export interface AllPostsComponentProps {
155 | variables?: AllPostsVariables;
156 | onReady?: import("stencil-apollo/dist/types/components/apollo-query/types").OnQueryReadyFn<
157 | AllPostsQuery,
158 | AllPostsVariables
159 | >;
160 | }
161 | export const AllPostsComponent: FunctionalComponent = (
162 | props,
163 | children
164 | ) => {
165 | return (
166 |
167 | {children}
168 |
169 | );
170 | };
171 |
--------------------------------------------------------------------------------
/stencil-client/src/global/app.css:
--------------------------------------------------------------------------------
1 | /* Document Level Styles */
2 |
3 | /*
4 | The imports below are needed to include our light dom css for global styles such as fonts and colors.
5 | You can comment out any of these imports if you do not need that css. For example, if you have your own
6 | global font family css then you can comment out the typography.css import.
7 | */
8 |
9 | /** Core CSS required for ionic components to work property */
10 | @import "~@ionic/core/css/core.css";
11 |
12 | /** Basic CSS for apps built with Ionic */
13 | @import "~@ionic/core/css/normalize.css";
14 | @import "~@ionic/core/css/structure.css";
15 | @import "~@ionic/core/css/typography.css";
16 |
17 | /** Optional CSS utils that can be commented out */
18 | @import "~@ionic/core/css/padding.css";
19 | @import "~@ionic/core/css/float-elements.css";
20 | @import "~@ionic/core/css/text-alignment.css";
21 | @import "~@ionic/core/css/text-transformation.css";
22 | @import "~@ionic/core/css/flex-utils.css";
23 |
24 | /*
25 | The CSS Variables below can be used to theme your app.
26 | For more info on CSS variables check out:
27 | https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables
28 |
29 | More info about color theming using Ionic:
30 | https://beta.ionicframework.com/docs/theming/color-generator
31 | */
32 |
--------------------------------------------------------------------------------
/stencil-client/src/global/app.ts:
--------------------------------------------------------------------------------
1 | import '@ionic/core';
2 |
3 | // import { setupConfig } from '@ionic/core';
4 |
5 | // setupConfig({
6 | // mode: 'ios'
7 | // });
8 |
9 | import 'stencil-apollo';
10 |
--------------------------------------------------------------------------------
/stencil-client/src/graphql/AddPost.graphql:
--------------------------------------------------------------------------------
1 | mutation AddPost($title: String, $content: String) {
2 | addPost(title: $title, content: $content) {
3 | id
4 | title
5 | content
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/stencil-client/src/graphql/AllPosts.graphql:
--------------------------------------------------------------------------------
1 | query AllPosts {
2 | allPosts{
3 | id
4 | title
5 | content
6 | author {
7 | username
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/stencil-client/src/helpers/accounts.ts:
--------------------------------------------------------------------------------
1 | import { AccountsClient } from '@accounts/client';
2 | import { AccountsClientPassword } from '@accounts/client-password';
3 | import GraphQLClient from '@accounts/graphql-client';
4 | import ApolloClient from 'apollo-boost';
5 |
6 | const apolloClient = new ApolloClient({
7 | request: async operation => {
8 | const tokens = await accountsClient.getTokens();
9 | if (tokens) {
10 | operation.setContext({
11 | headers: {
12 | 'accounts-access-token': tokens.accessToken
13 | }
14 | });
15 | }
16 | },
17 | uri: 'http://localhost:4000/graphql',
18 | });
19 |
20 | const accountsGraphQL = new GraphQLClient({ graphQLClient: apolloClient });
21 | const accountsClient = new AccountsClient({}, accountsGraphQL);
22 | const accountsPassword = new AccountsClientPassword(accountsClient);
23 |
24 | export { accountsClient, accountsGraphQL, accountsPassword, apolloClient };
25 |
--------------------------------------------------------------------------------
/stencil-client/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PWA Toolkit
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/stencil-client/src/interfaces.d.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ardatan/graphql-modules-accountsjs-boilerplate/7cc8c86c18bea17f7cd00f7ccc7518bf21074b90/stencil-client/src/interfaces.d.ts
--------------------------------------------------------------------------------
/stencil-client/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stencil-client",
3 | "short_name": "stencil-client",
4 | "start_url": "/",
5 | "display": "standalone",
6 | "icons": [{
7 | "src": "assets/icon/icon.png",
8 | "sizes": "512x512",
9 | "type": "image/png"
10 | }],
11 | "background_color": "#488aff",
12 | "theme_color": "#488aff"
13 | }
--------------------------------------------------------------------------------
/stencil-client/src/sw.js:
--------------------------------------------------------------------------------
1 | importScripts('workbox-v3.4.1/workbox-sw.js')
2 |
3 | /*
4 | This is our code to handle push events.
5 | */
6 | self.addEventListener('push', (event) => {
7 | console.log('[Service Worker] Push Received.');
8 | console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
9 |
10 | const title = 'Push Notification';
11 | const options = {
12 | body: `${event.data.text()}`,
13 | icon: 'images/icon.png',
14 | badge: 'images/badge.png'
15 | };
16 |
17 | event.waitUntil(self.registration.showNotification(title, options));
18 | });
19 |
20 | self.workbox.precaching.precacheAndRoute([]);
21 |
--------------------------------------------------------------------------------
/stencil-client/stencil.config.ts:
--------------------------------------------------------------------------------
1 | import { Config } from '@stencil/core';
2 |
3 | // https://stenciljs.com/docs/config
4 |
5 | export const config: Config = {
6 | outputTargets: [
7 | {
8 | type: 'www',
9 | serviceWorker: {
10 | swSrc: 'src/sw.js'
11 | }
12 | }
13 | ],
14 | globalScript: 'src/global/app.ts',
15 | globalStyle: 'src/global/app.css',
16 | commonjs: {
17 | include: ['./node_modules/**'],
18 | namedExports: {
19 | './node_modules/graphql-anywhere/lib/async.js': ['graphql'],
20 | './node_modules/graphql/language/parser.mjs': ['default'],
21 | './node_modules/apollo-utilities/lib/index.js': ['default']
22 | },
23 | extensions: ['.js', '.mjs']
24 | } as any,
25 | plugins: [
26 | {
27 | transform(source, id, context){
28 | return {
29 | code:
30 | source.replace(`var wrap = require('optimism').wrap;`, '')
31 | .replace('export { wrap }', 'export { wrap } from "optimism";'),
32 | id
33 | }
34 | }
35 | }
36 | ]
37 | };
38 |
--------------------------------------------------------------------------------
/stencil-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "allowUnreachableCode": false,
5 | "declaration": false,
6 | "experimentalDecorators": true,
7 | "lib": [
8 | "dom",
9 | "es2017",
10 | "esnext.asynciterable"
11 | ],
12 | "moduleResolution": "node",
13 | "module": "esnext",
14 | "target": "es2017",
15 | "noUnusedLocals": true,
16 | "noUnusedParameters": true,
17 | "jsx": "react",
18 | "jsxFactory": "h"
19 | },
20 | "include": [
21 | "src"
22 | ],
23 | "exclude": [
24 | "node_modules",
25 | "../node_modules"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------