├── .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 |
2 | 3 | 4 | Add Post 5 | 6 | {{error}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
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 |
2 | 3 | 4 | Login 5 | 6 | {{error}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
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 |
2 | 3 | 4 | Register 5 | 6 | {{error}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
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 |
52 | 53 | Add Post 54 | 55 |
56 |

57 | 60 | this.setState({ newPost: { ...this.state.newPost, title: e.target.value } })} /> 61 |

62 |

63 | 66 | this.setState({ newPost: { ...this.state.newPost, content: e.target.value } })} /> 67 |

68 |

69 | { 70 | e.preventDefault(); 71 | addPost({ 72 | variables: this.state.newPost 73 | }); 74 | }} /> 75 |

76 |
77 |
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 |
69 | 70 | Login 71 | 72 | 73 | Username 74 | 75 | 76 | 77 | Password 78 | 85 | 86 | 89 | {error && } 90 | 91 | 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 |
68 | 69 | Sign up 70 | 71 | 72 | Username 73 | 74 | 75 | 76 | Password 77 | 78 | 79 | 82 | {error && } 83 | 84 | 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 |
53 | 54 | Add Post 55 | 56 |
57 |

58 | 61 | this.title = e.target['value'] } /> 62 |

63 |

64 | 67 | this.content = e.target['value']} /> 68 |

69 |

70 | { 71 | e.preventDefault(); 72 | addPost({ 73 | variables: { 74 | title: this.title, 75 | content: this.content 76 | } 77 | }); 78 | }} /> 79 |

80 |
81 |
82 | ) 83 | } /> 84 |

85 | Posts; 86 |

87 |
    88 | { 90 | if (loading) { 91 | return

    Loading...

    92 | } 93 | if( errors && errors.length ) { 94 | return

    Error: {errors.join('\n')}

    95 | } 96 | if (data && data.allPosts ) { 97 | return data.allPosts.map((post, index) => ( 98 |
  • 99 |

    100 | {post.title} 101 |

    102 |

    103 | {post.content} 104 |

    105 |

    106 | {post.author && post.author.username} 107 |

    108 |
  • 109 | )) 110 | } 111 | } 112 | } /> 113 |
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 | --------------------------------------------------------------------------------