├── .github └── workflows │ ├── auth-service.yml │ ├── dockerimage.yml │ ├── npmpackages.yml │ └── text-service.yml ├── .gitignore ├── LICENSE ├── README.md ├── auth-service ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── .snyk ├── Dockerfile ├── README.md ├── nest-cli.json ├── package.json ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.interface.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── auth │ │ ├── auth.controller.spec.ts │ │ ├── auth.controller.ts │ │ ├── auth.module.ts │ │ ├── auth.service.ts │ │ ├── constants.ts │ │ ├── jwt-auth.guard.ts │ │ ├── jwt.strategy.ts │ │ ├── local-auth.guard.ts │ │ ├── local.strategy.ts │ │ ├── twitter.strategy.ts │ │ └── types.ts │ ├── config │ │ └── configuration.ts │ ├── consts │ │ └── index.ts │ ├── main.ts │ ├── users │ │ ├── user.entity.ts │ │ ├── users.controller.spec.ts │ │ ├── users.controller.ts │ │ ├── users.module.ts │ │ └── users.service.ts │ ├── utils.ts │ └── validators │ │ └── index.ts ├── test │ ├── app.e2e-spec.ts │ ├── jest-e2e.json │ └── users.e2e-sepc.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock ├── chrome-extension ├── background.js ├── images │ ├── icon128.png │ ├── icon16.png │ ├── icon256.png │ ├── icon32.png │ ├── icon48.png │ └── icon_big.png ├── manifest.json ├── options.html ├── options.js ├── popup.html └── popup.js ├── contribiuting.md ├── docker-compose.yml ├── elastic ├── README.md └── docker-compose.yml ├── logo.png ├── mysql ├── README.md └── docker-compose.yml ├── package.json ├── run_auth_server.sh ├── text-ml-service └── Dockerfile ├── text-service ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── Dockerfile ├── nest-cli.json ├── package.json ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── config │ │ └── configuration.ts │ ├── elastic │ │ └── index.ts │ ├── main.ts │ └── search │ │ ├── search.controller.spec.ts │ │ ├── search.controller.ts │ │ ├── search.module.ts │ │ └── search.service.ts ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock ├── tsconfig.json ├── type-test ├── index.js ├── index.ts ├── package-lock.json └── package.json ├── types ├── bb-tests.ts ├── index.d.ts ├── index.js ├── index.ts ├── package-lock.json ├── package.json ├── package.json.old ├── tsconfig.json └── tslint.json ├── web-ui ├── .gitignore ├── .prettierrc ├── .storybook │ ├── tsconfig.json │ └── webpack.config.js ├── Dockerfile ├── Dockerfile-dev ├── README.md ├── package-lock.json ├── package.json ├── semantic.json ├── src │ ├── app │ │ ├── components │ │ │ ├── CustomInput.tsx │ │ │ ├── Footer │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ │ ├── Header │ │ │ │ └── index.tsx │ │ │ ├── Navigation.tsx │ │ │ ├── Password.tsx │ │ │ ├── PrivateRoute.tsx │ │ │ ├── TodoItem │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ │ ├── TodoList │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ │ ├── TodoTextInput │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ │ └── index.ts │ │ ├── constants │ │ │ ├── index.ts │ │ │ ├── stores.ts │ │ │ └── todos.ts │ │ ├── containers │ │ │ ├── Root │ │ │ │ └── index.tsx │ │ │ └── TodoApp │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ ├── history.ts │ │ ├── index.tsx │ │ ├── models │ │ │ ├── TodoModel.ts │ │ │ └── index.ts │ │ ├── routes │ │ │ ├── authentication │ │ │ │ ├── components │ │ │ │ │ ├── FacebookButton.tsx │ │ │ │ │ ├── GithubButton.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ ├── Register.tsx │ │ │ │ │ ├── TabsWrapper.tsx │ │ │ │ │ └── TwitterButton.tsx │ │ │ │ ├── constants │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ └── main │ │ │ │ └── index.ts │ │ ├── stores │ │ │ ├── RouterStore.ts │ │ │ ├── TodoStore.ts │ │ │ ├── createStore.ts │ │ │ └── index.ts │ │ ├── styledComponents │ │ │ └── Sidebar │ │ │ │ ├── Sidebar.js │ │ │ │ ├── index.tsx │ │ │ │ └── sidebar.story.js │ │ └── theme │ │ │ ├── dark.ts │ │ │ └── light.ts │ ├── assets │ │ ├── favicon.ico │ │ └── index.html │ └── main.tsx ├── tsconfig.json ├── types │ └── global.d.ts ├── webpack.config.js └── yarn.lock └── yarn.lock /.github/workflows/auth-service.yml: -------------------------------------------------------------------------------- 1 | name: Auth-service 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | env: 10 | working-directory: ./auth-service 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Build, lint and tests 14 | run: | 15 | yarn 16 | yarn run lint 17 | # yarn run test 18 | # yarn run test:cov 19 | working-directory: ${{ env.working-directory }} 20 | - name: Build the Auth service Docker image and publish to GitHub Packages 21 | working-directory: ${{ env.working-directory }} 22 | run: | 23 | docker build . --tag docker.pkg.github.com/brainbackup/bb8/$SERVICE_NAME:$VERSION 24 | docker login docker.pkg.github.com --username ynahmany --password ${{ secrets.GITHUB_TOKEN }} 25 | docker push docker.pkg.github.com/brainbackup/bb8/$SERVICE_NAME:$VERSION 26 | env: 27 | VERSION: latest 28 | SERVICE_NAME: auth-service 29 | -------------------------------------------------------------------------------- /.github/workflows/dockerimage.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'releases/**' 7 | 8 | jobs: 9 | 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Build the $SERVICE_NAME Docker image and publish to GitHub Packages 16 | run: | 17 | docker build ./$SERVICE_NAME --file ./$SERVICE_NAME/Dockerfile --tag docker.pkg.github.com/brainbackup/code-snippet/$SERVICE_NAME:$VERSION 18 | docker login docker.pkg.github.com --username ynahmany --password ${{ secrets.GITHUB_TOKEN }} 19 | docker push docker.pkg.github.com/brainbackup/code-snippet/$SERVICE_NAME:$VERSION 20 | env: 21 | VERSION: latest 22 | SERVICE_NAME: web-ui 23 | - name: Build the $SERVICE_NAME Docker image and publish to GitHub Packages 24 | run: | 25 | docker build ./$SERVICE_NAME --file ./$SERVICE_NAME/Dockerfile --tag docker.pkg.github.com/brainbackup/code-snippet/$SERVICE_NAME:$VERSION 26 | docker login docker.pkg.github.com --username ynahmany --password ${{ secrets.GITHUB_TOKEN }} 27 | docker push docker.pkg.github.com/brainbackup/code-snippet/$SERVICE_NAME:$VERSION 28 | env: 29 | VERSION: latest 30 | SERVICE_NAME: search-service -------------------------------------------------------------------------------- /.github/workflows/npmpackages.yml: -------------------------------------------------------------------------------- 1 | name: NPM packages 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'releases/**' 7 | 8 | jobs: 9 | 10 | publish-npm-packges: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 12 17 | registry-url: https://registry.npmjs.org/ 18 | - run: | 19 | cd ./types 20 | npm version patch 21 | npm publish --access public 22 | env: 23 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 24 | -------------------------------------------------------------------------------- /.github/workflows/text-service.yml: -------------------------------------------------------------------------------- 1 | name: Text-service 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | env: 10 | working-directory: ./text-service 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Build, lint and tests 14 | run: | 15 | yarn 16 | yarn run lint 17 | yarn run test 18 | yarn run test:cov 19 | working-directory: ${{ env.working-directory }} 20 | - name: Build the Text service Docker image and publish to GitHub Packages 21 | working-directory: ${{ env.working-directory }} 22 | run: | 23 | docker build . --tag docker.pkg.github.com/brainbackup/bb8/$SERVICE_NAME:$VERSION 24 | docker login docker.pkg.github.com --username ynahmany --password ${{ secrets.GITHUB_TOKEN }} 25 | docker push docker.pkg.github.com/brainbackup/bb8/$SERVICE_NAME:$VERSION 26 | env: 27 | VERSION: latest 28 | SERVICE_NAME: text-service 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | .env 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yohay Nahmany 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BB8: modular, powerful and extendable project for memory improvment 2 | 3 | brain-backup 4 | 5 | 6 | * * * 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 8 | ![CI](https://github.com/BrainBackup/code-snippet/workflows/CI/badge.svg?branch=master) 9 | ![Docker Images Creation](https://github.com/BrainBackup/code-snippet/workflows/Docker%20Image%20CI/badge.svg?branch=master) 10 | ![Auth-service](https://github.com/BrainBackup/bb8/workflows/Auth-service/badge.svg) 11 | ![NPM packages](https://github.com/BrainBackup/bb8/workflows/NPM%20packages/badge.svg) 12 | ![Text-service](https://github.com/BrainBackup/bb8/workflows/Text-service/badge.svg) 13 | 14 | ## Overview 15 | 16 | * Provide users an ability to save code snippets, highlighted data from anywhere on the web. 17 | * Code snippets will be provided once the user starts typing in an IDE, searching, and analytics using a web application. 18 | * The users will be able to add data using chrome-extension, keyboard shortcut, right-click and simple user interface. 19 | 20 | ## Simple development process 21 | 22 | ### local development using docker 23 | 24 | `docker-compose build` 25 | 26 | `docker-compose up` 27 | 28 | * process.env.NODE_ENV - development, test and production - should be passed as env variable 29 | 30 | ### Authentication service 31 | 32 | * Require mysql: `cd mysql && docker-compose up` 33 | * Running locally: `cd auth-service && yarn start:dev` 34 | * Swagger: go to `http://localhost:3010/api/` 35 | * Health check: `http://localhost:3010/health` 36 | 37 | ### Text service 38 | 39 | * Require elastic: `cd elastic && docker-compose up` 40 | * Running locally: `cd text-service && yarn start:dev` 41 | * Swagger: go to `http://localhost:3009/api/` 42 | * Health check: `http://localhost:3009/health` 43 | 44 | ### Chrome extension 45 | 46 | * Side loading the chrome extension: go to chrome://extensions, click on Load unpacked, select the chrome-extension directoy 47 | * Open issue: the header should include `chrome-extension::` and follow up the subject of the issue. 48 | * TODO: Automatic way to update the app. 49 | 50 | ### Test NLP service 51 | 52 | * MLQA reserch from Facebook® should be a good start. 53 | 54 | -------------------------------------------------------------------------------- /auth-service/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: [ 9 | 'plugin:@typescript-eslint/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'prettier', 12 | 'prettier/@typescript-eslint', 13 | ], 14 | root: true, 15 | env: { 16 | node: true, 17 | jest: true, 18 | }, 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/no-explicit-any': 'off', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /auth-service/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /auth-service/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /auth-service/.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.14.1 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-567746: 7 | - '@nestjs/swagger > lodash': 8 | patched: '2020-04-30T23:42:18.842Z' 9 | -------------------------------------------------------------------------------- /auth-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.16 2 | WORKDIR /usr/app 3 | COPY . . 4 | RUN yarn 5 | RUN yarn build 6 | CMD yarn start:prod -------------------------------------------------------------------------------- /auth-service/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master 6 | [travis-url]: https://travis-ci.org/nestjs/nest 7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux 8 | [linux-url]: https://travis-ci.org/nestjs/nest 9 | 10 |

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

11 |

12 | NPM Version 13 | Package License 14 | NPM Downloads 15 | Travis 16 | Linux 17 | Coverage 18 | Gitter 19 | Backers on Open Collective 20 | Sponsors on Open Collective 21 | 22 | 23 |

24 | 26 | 27 | ## Description 28 | 29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 30 | 31 | ## Installation 32 | 33 | ```bash 34 | $ yarn 35 | ``` 36 | 37 | ## Running the app 38 | 39 | ```bash 40 | # development 41 | $ yarn run start 42 | 43 | # watch mode 44 | $ yarn run start:dev 45 | 46 | # production mode 47 | $ yarn run start:prod 48 | ``` 49 | 50 | ## Test 51 | 52 | ```bash 53 | # unit tests 54 | $ yarn run test 55 | 56 | # e2e tests 57 | $ yarn run test:e2e 58 | 59 | # test coverage 60 | $ yarn run test:cov 61 | ``` 62 | # Swagger 63 | 64 | $ http://localhost:3000/api/ -------------------------------------------------------------------------------- /auth-service/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /auth-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-service", 3 | "version": "0.0.1", 4 | "description": "Authentication service", 5 | "author": "Yohay Nahmany", 6 | "private": true, 7 | "license": "MIT", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json", 22 | "snyk-protect": "snyk protect", 23 | "prepare": "yarn run snyk-protect" 24 | }, 25 | "dependencies": { 26 | "@hapi/joi": "^17.1.1", 27 | "@nestjs/common": "^8.0.7", 28 | "@nestjs/config": "^0.6.2", 29 | "@nestjs/core": "^7.6.17", 30 | "@nestjs/jwt": "^10.0.0", 31 | "@nestjs/passport": "^7.1.5", 32 | "@nestjs/platform-express": "^7.6.17", 33 | "@nestjs/swagger": "^4.8.1", 34 | "@nestjs/typeorm": "^7.1.5", 35 | "@types/passport-local": "^1.0.33", 36 | "mysql": "^2.18.1", 37 | "n": "^6.7.1", 38 | "passport": "^0.4.1", 39 | "passport-jwt": "^4.0.1", 40 | "passport-local": "^1.0.0", 41 | "passport-twitter": "^1.0.4", 42 | "reflect-metadata": "^0.1.13", 43 | "rimraf": "^3.0.2", 44 | "rxjs": "^6.6.7", 45 | "snyk": "^1.611.0", 46 | "swagger-ui-express": "^4.1.6", 47 | "typeorm": "^0.2.33" 48 | }, 49 | "devDependencies": { 50 | "@nestjs/cli": "^7.0.0", 51 | "@nestjs/schematics": "^7.0.0", 52 | "@nestjs/testing": "^7.0.0", 53 | "@types/express": "^4.17.3", 54 | "@types/hapi__joi": "^16.0.12", 55 | "@types/jest": "25.1.4", 56 | "@types/node": "^13.9.1", 57 | "@types/passport-jwt": "^3.0.3", 58 | "@types/supertest": "^2.0.8", 59 | "@typescript-eslint/eslint-plugin": "^2.23.0", 60 | "@typescript-eslint/parser": "^2.23.0", 61 | "eslint": "^6.8.0", 62 | "eslint-config-prettier": "^6.10.0", 63 | "eslint-plugin-import": "^2.20.1", 64 | "jest": "^25.1.0", 65 | "prettier": "^1.19.1", 66 | "supertest": "^4.0.2", 67 | "ts-jest": "25.2.1", 68 | "ts-loader": "^6.2.1", 69 | "ts-node": "^8.6.2", 70 | "tsconfig-paths": "^3.9.0", 71 | "typescript": "^3.7.4" 72 | }, 73 | "jest": { 74 | "moduleFileExtensions": [ 75 | "js", 76 | "json", 77 | "ts" 78 | ], 79 | "rootDir": "src", 80 | "testRegex": ".spec.ts$", 81 | "transform": { 82 | "^.+\\.(t|j)s$": "ts-jest" 83 | }, 84 | "coverageDirectory": "../coverage", 85 | "testEnvironment": "node" 86 | }, 87 | "snyk": true 88 | } 89 | -------------------------------------------------------------------------------- /auth-service/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('Health check function', () => { 19 | expect(appController.checkHealth()).toStrictEqual({"services": [{"name": "mysql", "status": "UP", "version": "10.1.2"}], "status": "UP"}); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /auth-service/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Request, Get, Post, UseGuards } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | import { LocalAuthGuard } from './auth/local-auth.guard'; 4 | import { JwtAuthGuard } from './auth/jwt-auth.guard'; 5 | import { HealthCheck } from './app.interface'; 6 | import { AuthService } from './auth/auth.service'; 7 | 8 | @Controller() 9 | export class AppController { 10 | constructor( 11 | private readonly appService: AppService, 12 | private readonly authService: AuthService 13 | ) {} 14 | 15 | @Get('/health') 16 | checkHealth(): HealthCheck { 17 | return this.appService.checkHealth(); 18 | } 19 | @UseGuards(LocalAuthGuard) 20 | @Post('auth/login') 21 | async login(@Request() req) { 22 | return this.authService.login(req.user); 23 | } 24 | 25 | @UseGuards(JwtAuthGuard) 26 | @Get('profile') 27 | getProfile(@Request() req) { 28 | return req.user; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /auth-service/src/app.interface.ts: -------------------------------------------------------------------------------- 1 | export enum Status { 2 | UP = `UP`, 3 | DOWN = `DOWN` 4 | } 5 | export interface Service { 6 | name: string; 7 | version: string; 8 | status: Status; 9 | } 10 | export interface HealthCheck { 11 | status: Status; 12 | services: Service[] 13 | } -------------------------------------------------------------------------------- /auth-service/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, HttpModule } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { AuthController } from './auth/auth.controller'; 5 | import { UsersModule } from './users/users.module'; 6 | import { AuthModule } from './auth/auth.module'; 7 | import { TypeOrmModule } from '@nestjs/typeorm'; 8 | import { ConfigModule } from '@nestjs/config'; 9 | import configuration from './config/configuration'; 10 | const Configuration = configuration(); 11 | 12 | @Module({ 13 | imports: [ 14 | HttpModule, 15 | TypeOrmModule.forRoot({ 16 | "type": "mysql", 17 | "host": Configuration.database.host, 18 | "port": Configuration.database.port, 19 | "username": Configuration.database.username, 20 | "password": Configuration.database.password, 21 | "database": Configuration.database.name, 22 | "entities": ["dist/**/*.entity.js"], 23 | "synchronize": true 24 | }), 25 | UsersModule, 26 | AuthModule, 27 | ConfigModule.forRoot({ 28 | load: [configuration] 29 | }) 30 | ], 31 | controllers: [AppController, AuthController], 32 | providers: [AppService], 33 | }) 34 | export class AppModule{} 35 | -------------------------------------------------------------------------------- /auth-service/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { HealthCheck, Status } from './app.interface'; 3 | @Injectable() 4 | export class AppService { 5 | checkHealth(): HealthCheck { 6 | // the status of the connections to the infrastructure services used by the service instance 7 | // the status of the host, e.g. disk space 8 | // application specific logic 9 | const healthCheck: HealthCheck = { 10 | status: Status.UP, 11 | services: [{ name: 'mysql', version: '10.1.2', status: Status.UP }] 12 | } 13 | return healthCheck; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /auth-service/src/auth/auth.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AuthController } from './auth.controller'; 3 | import { AuthService } from './auth.service'; 4 | import { JwtService } from '@nestjs/jwt'; 5 | import { UsersModule } from '../users/users.module'; 6 | import { AuthModule } from './auth.module'; 7 | 8 | describe('AuthController', () => { 9 | let authController: AuthController; 10 | 11 | beforeEach(async () => { 12 | const app: TestingModule = await Test.createTestingModule({ 13 | imports: [AuthModule], 14 | controllers: [AuthController], 15 | providers: [AuthService, JwtService], 16 | }).compile(); 17 | 18 | authController = app.get(AuthController); 19 | }); 20 | 21 | describe('root', () => { 22 | it('Login', () => { 23 | expect(authController).toBeDefined(); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /auth-service/src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body, UsePipes } from '@nestjs/common'; 2 | import * as Joi from '@hapi/joi'; 3 | import { AuthService } from './auth.service'; 4 | import { ILogin } from './types'; 5 | import { JoiValidationPipe } from '../validators'; 6 | 7 | const createLoginSchema = () => 8 | Joi.object({ 9 | mailAddress: Joi.string().required(), 10 | password: Joi.string().required() 11 | }); 12 | @Controller('/auth') 13 | export class AuthController { 14 | constructor(private readonly authService: AuthService) {} 15 | 16 | @Post('login') 17 | @UsePipes(new JoiValidationPipe(createLoginSchema())) 18 | async login(@Body() user: ILogin) { 19 | return await this.authService.login(user); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /auth-service/src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AuthService } from './auth.service'; 3 | import { UsersModule } from '../users/users.module'; 4 | import { JwtModule } from '@nestjs/jwt'; 5 | import { PassportModule } from '@nestjs/passport'; 6 | import { LocalStrategy } from './local.strategy'; 7 | import { jwtConstants } from './constants'; 8 | import { JwtStrategy } from './jwt.strategy'; 9 | import { TwitterStrategy } from './twitter.strategy'; 10 | import { ConfigModule } from '@nestjs/config'; 11 | 12 | 13 | @Module({ 14 | imports: [ 15 | PassportModule.register({ defaultStrategy: 'jwt' }), 16 | JwtModule.register({ 17 | secret: jwtConstants.secret, 18 | signOptions: { expiresIn: '60s' }, 19 | }), 20 | UsersModule, 21 | ConfigModule 22 | ], 23 | providers: [AuthService, LocalStrategy, JwtStrategy, TwitterStrategy], 24 | exports: [AuthService], 25 | }) 26 | export class AuthModule {} 27 | -------------------------------------------------------------------------------- /auth-service/src/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { UsersService } from '../users/users.service'; 3 | import { JwtService } from '@nestjs/jwt'; 4 | import { ILogin } from './types'; 5 | import { hashPassword } from '../utils'; 6 | 7 | @Injectable() 8 | export class AuthService { 9 | constructor( 10 | private jwtService: JwtService, 11 | private usersService: UsersService, 12 | ) {} 13 | 14 | async validateUser(mailAddress: string, password: string): Promise { 15 | const user = await this.usersService.getUserByMailAddress(mailAddress); 16 | if (user && user.isActive && user.password === hashPassword(password, user.salt)) { 17 | return { 18 | id: user.id, 19 | fullName: user.fullName, 20 | isAdmin: user.isAdmin, 21 | email: user.email, 22 | username: user.username, 23 | createdAt: user.createdAt 24 | } 25 | } 26 | throw new UnauthorizedException(); 27 | } 28 | 29 | async login(user: ILogin) { 30 | const res = await this.validateUser(user.mailAddress, user.password); 31 | return this.jwtService.sign(res); 32 | } 33 | } -------------------------------------------------------------------------------- /auth-service/src/auth/constants.ts: -------------------------------------------------------------------------------- 1 | export const jwtConstants = { 2 | secret: 'secretKey', 3 | }; -------------------------------------------------------------------------------- /auth-service/src/auth/jwt-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | 4 | @Injectable() 5 | export class JwtAuthGuard extends AuthGuard('jwt') {} 6 | -------------------------------------------------------------------------------- /auth-service/src/auth/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { ExtractJwt, Strategy } from 'passport-jwt'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Injectable } from '@nestjs/common'; 4 | import { jwtConstants } from './constants'; 5 | 6 | @Injectable() 7 | export class JwtStrategy extends PassportStrategy(Strategy) { 8 | constructor() { 9 | super({ 10 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 11 | ignoreExpiration: false, 12 | secretOrKey: jwtConstants.secret, 13 | }); 14 | } 15 | 16 | async validate(payload: any) { 17 | return { userId: payload.sub, username: payload.username }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /auth-service/src/auth/local-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | 4 | @Injectable() 5 | export class LocalAuthGuard extends AuthGuard('local') {} -------------------------------------------------------------------------------- /auth-service/src/auth/local.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Strategy } from 'passport-local'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 4 | import { AuthService } from './auth.service'; 5 | 6 | @Injectable() 7 | export class LocalStrategy extends PassportStrategy(Strategy) { 8 | constructor(private authService: AuthService) { 9 | super(); 10 | } 11 | 12 | async validate(username: string, password: string): Promise { 13 | const user = await this.authService.validateUser(username, password); 14 | if (!user) { 15 | throw new UnauthorizedException(); 16 | } 17 | return user; 18 | } 19 | } -------------------------------------------------------------------------------- /auth-service/src/auth/twitter.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Strategy } from 'passport-twitter'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Injectable } from '@nestjs/common'; 4 | import { ConfigService } from '@nestjs/config'; 5 | 6 | @Injectable() 7 | export class TwitterStrategy extends PassportStrategy(Strategy) { 8 | constructor(private configService: ConfigService) { 9 | super({ 10 | consumerKey: configService.get('twitter.key'), 11 | consumerSecret: configService.get('twitter.secret'), 12 | callbackURL: configService.get('twitter.callback') 13 | }); 14 | } 15 | 16 | async validate(payload: any) { 17 | return { userId: payload.sub, username: payload.username }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /auth-service/src/auth/types.ts: -------------------------------------------------------------------------------- 1 | export interface ILogin { 2 | mailAddress: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /auth-service/src/config/configuration.ts: -------------------------------------------------------------------------------- 1 | interface IDatabase { 2 | host: string; 3 | port: number; 4 | username: string; 5 | password: string; 6 | name: string; 7 | } 8 | interface Auth { 9 | key: string; 10 | secret: string; 11 | callback: string; 12 | } 13 | interface Configuration { 14 | port: number, 15 | database: IDatabase, 16 | twitter?: Auth 17 | } 18 | interface ConfigurationResult { 19 | development: Configuration; 20 | testing: Configuration; 21 | production?: Configuration; 22 | } 23 | const configuration: ConfigurationResult = { 24 | development: { 25 | port: parseInt(process.env.PORT, 10) || 3010, 26 | database: { 27 | host: 'localhost', 28 | port: 3306, 29 | username: 'root', 30 | password: 'w3lc0me!', 31 | name: 'auth_service', 32 | }, 33 | twitter: { 34 | key: '5GvNgRMSQFaab70EBfV5psXW8', 35 | secret: 'D6qXsHHpJGk2427lJ9AveC3XBjcFfyBp3ZaaEB95y65RGm7Zb8', 36 | callback: 'http://localhost:3010' 37 | } 38 | }, 39 | testing: { 40 | port: parseInt(process.env.PORT, 10) || 3010, 41 | database: { 42 | host: 'localhost', 43 | port: 3306, 44 | username: 'root', 45 | password: 'w3lc0me!', 46 | name: 'auth_service', 47 | } 48 | } 49 | }; 50 | export default (): Configuration => configuration[process.env.NODE_ENV] || configuration['development']; 51 | -------------------------------------------------------------------------------- /auth-service/src/consts/index.ts: -------------------------------------------------------------------------------- 1 | export const DATABASE_CONNECTION = `DATABASE_CONNECTION`; -------------------------------------------------------------------------------- /auth-service/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 3 | import { AppModule } from './app.module'; 4 | import configuration from './config/configuration'; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule); 8 | const Configuration = configuration(); 9 | const options = new DocumentBuilder() 10 | .setTitle('Auth Service') 11 | .setDescription('Auth service should provide all apis for authentication and authorization for the bb projects') 12 | .setVersion('1.0') 13 | .addTag('auth') 14 | .build(); 15 | const document = SwaggerModule.createDocument(app, options); 16 | SwaggerModule.setup('api', app, document); 17 | app.enableCors(); 18 | await app.listen(Configuration.port); 19 | } 20 | bootstrap(); 21 | -------------------------------------------------------------------------------- /auth-service/src/users/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | 5 | @Entity() 6 | export class Users { 7 | 8 | @PrimaryGeneratedColumn('increment') id: number; 9 | @ApiProperty() @Column({ length: 25 }) fullName : string; 10 | @ApiProperty() @Column() email: string; 11 | @ApiProperty() @Column() username: string; 12 | @ApiProperty() @Column() salt: string; 13 | @ApiProperty() @Column() password: string; 14 | @ApiProperty() @Column() createdAt : Date; 15 | @ApiProperty() @Column() confirmationToken : string; 16 | @ApiProperty() @Column() isVerified : boolean; 17 | @ApiProperty() @Column() isAdmin : boolean; 18 | @ApiProperty() @Column() isActive : boolean; 19 | } 20 | -------------------------------------------------------------------------------- /auth-service/src/users/users.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { UsersController } from './users.controller'; 3 | import { UsersService } from './users.service'; 4 | 5 | describe('UsersController', () => { 6 | let usersController: UsersController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [UsersController], 11 | providers: [UsersService], 12 | }).compile(); 13 | 14 | usersController = app.get(UsersController); 15 | }); 16 | 17 | describe('Get', () => { 18 | it('', () => { 19 | expect(usersController.get(0)).toBeFalsy(); 20 | }) 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /auth-service/src/users/users.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body, Get, Put, Delete, Param, UsePipes} from '@nestjs/common'; 2 | import { UsersService } from './users.service'; 3 | import { Users } from './user.entity'; 4 | import { ApiQuery, ApiParam } from '@nestjs/swagger'; 5 | import { v4 as uuid } from 'uuid'; 6 | import { JoiValidationPipe } from '../validators'; 7 | import { createSalt, hashPassword } from '../utils'; 8 | import * as Joi from '@hapi/joi'; 9 | 10 | interface WebRegister { 11 | firstName: string; 12 | lastName: string; 13 | mailAddress: string; 14 | password: string; 15 | } 16 | const createWebRegisterSchema = () => 17 | Joi.object({ 18 | firstName: Joi.string().required(), 19 | lastName: Joi.string().required(), 20 | mailAddress: Joi.string().email().required(), 21 | password: Joi.string().required() 22 | }); 23 | @Controller('users') 24 | export class UsersController { 25 | 26 | constructor(private service: UsersService) { } 27 | 28 | @Get('/all') 29 | getAll() { 30 | return this.service.getUsers(); 31 | } 32 | @Get(':id') 33 | @ApiParam({ name: 'id', type: Number }) 34 | get(@Param('id') id) { 35 | return this.service.getUser(id); 36 | } 37 | 38 | @Post() 39 | @UsePipes(new JoiValidationPipe(createWebRegisterSchema())) 40 | create(@Body() user: WebRegister) { 41 | // TODO: validate it has firstname, lastname, mail and password. 42 | const salt = createSalt(); 43 | const _user: Users = { 44 | id: 0, 45 | fullName: `${user.firstName} ${user.lastName}`, 46 | email: user.mailAddress, 47 | username: uuid(), 48 | salt, 49 | password: hashPassword(user.password, salt), 50 | isVerified: false, 51 | createdAt: new Date, 52 | isAdmin: false, 53 | isActive: true, 54 | confirmationToken: createSalt() 55 | } 56 | return this.service.createUser(_user); 57 | } 58 | 59 | @Put() 60 | @ApiQuery({ name: 'id'}) 61 | update(@Param() param: Partial, @Body() user: Users) { 62 | return this.service.updateUser(param.id, user); 63 | } 64 | 65 | @Delete(':id') 66 | @ApiParam({ name: 'id', type: Number }) 67 | deleteUser(@Param() params) { 68 | return this.service.deleteUser(params.id); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /auth-service/src/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { UsersService } from './users.service'; 4 | import { UsersController } from './users.controller'; 5 | import { Users } from './user.entity'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Users])], 9 | providers: [UsersService], 10 | controllers: [UsersController], 11 | exports: [UsersService] 12 | }) 13 | 14 | export class UsersModule { } -------------------------------------------------------------------------------- /auth-service/src/users/users.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | import { Users } from './user.entity'; 5 | 6 | @Injectable() 7 | export class UsersService { 8 | 9 | constructor(@InjectRepository(Users) private usersRepository: Repository) { } 10 | 11 | async getUsers(): Promise { 12 | return await this.usersRepository.find({ 13 | order: { 14 | salt: "ASC" 15 | } 16 | }); 17 | } 18 | 19 | async getUser(_id: number): Promise { 20 | return await this.usersRepository.find({ 21 | select: ["fullName", "isActive", "email"], 22 | where: [{ "id": _id }] 23 | }); 24 | } 25 | async getUserByUsername(username: string): Promise { 26 | return await this.usersRepository.findOne({ 27 | where: [{ "username": username }] 28 | }); 29 | } 30 | async getUserByMailAddress(mailAddress: string): Promise { 31 | return await this.usersRepository.findOne({ 32 | where: [{ email: mailAddress }] 33 | }) 34 | } 35 | async createUser(user: Users) { 36 | this.usersRepository.save(user); 37 | } 38 | async updateUser(_id: number, user: Users) { 39 | const oldUser: Users = await this.usersRepository.findOne(_id); 40 | this.usersRepository.save({ 41 | ...oldUser, 42 | ...user 43 | }); 44 | } 45 | 46 | async deleteUser(user: Users) { 47 | this.usersRepository.delete(user); 48 | } 49 | } -------------------------------------------------------------------------------- /auth-service/src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | 3 | export const createSalt = () => 4 | crypto.randomBytes(16) 5 | .toString('hex') 6 | .slice(0, 32); 7 | export const hashPassword = (password: string, salt: string) => { 8 | const hash = crypto.createHmac('sha512', salt); /** Hashing algorithm sha512 */ 9 | hash.update(password); 10 | return hash.digest('hex'); 11 | } -------------------------------------------------------------------------------- /auth-service/src/validators/index.ts: -------------------------------------------------------------------------------- 1 | import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; 2 | import { ObjectSchema } from '@hapi/joi'; 3 | 4 | @Injectable() 5 | export class JoiValidationPipe implements PipeTransform { 6 | constructor(private schema: ObjectSchema) {} 7 | 8 | transform(value: any) { 9 | const { error } = this.schema.validate(value); 10 | if (error) { 11 | throw new BadRequestException('Validation failed'); 12 | } 13 | return value; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /auth-service/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/health (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/health') 21 | .expect(200); 22 | }); 23 | it('/auth (GET)', () => { 24 | return request(app.getHttpServer()) 25 | .get('/auth') 26 | .expect(200); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /auth-service/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /auth-service/test/users.e2e-sepc.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from '../src/app.module'; 5 | import { UsersModule } from '../src/users/users.module'; 6 | import { TypeOrmModule } from '@nestjs/typeorm'; 7 | 8 | 9 | describe('AppController (e2e)', () => { 10 | let app: INestApplication; 11 | 12 | beforeEach(async () => { 13 | const moduleFixture: TestingModule = await Test.createTestingModule({ 14 | imports: [ 15 | AppModule, 16 | TypeOrmModule.forRoot(), 17 | UsersModule 18 | ], 19 | }).compile(); 20 | 21 | app = moduleFixture.createNestApplication(); 22 | await app.init(); 23 | }); 24 | 25 | it('/users/:id (GET)', () => { 26 | return request(app.getHttpServer()) 27 | .get('/users/15') 28 | .expect(200); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /auth-service/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /auth-service/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /chrome-extension/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(function() { 2 | chrome.storage.sync.set({color: '#3aa757'}, function() { 3 | console.log('The color is green.'); 4 | }); 5 | chrome.declarativeContent.onPageChanged.removeRules(undefined, function() { 6 | chrome.declarativeContent.onPageChanged.addRules([{ 7 | conditions: [new chrome.declarativeContent.PageStateMatcher({ 8 | pageUrl: {hostEquals: 'developer.chrome.com'}, 9 | }) 10 | ], 11 | actions: [new chrome.declarativeContent.ShowPageAction()] 12 | }]); 13 | }); 14 | 15 | chrome.contextMenus.create({ 16 | id: 'saveCodeSnippet', 17 | title: 'Save code snippet', 18 | contexts: ['all'] 19 | }); 20 | chrome.contextMenus.create({ 21 | id: 'highlight', 22 | title: 'Highlight', 23 | contexts: ['all'] 24 | }); 25 | chrome.contextMenus.onClicked.addListener(function(data) { 26 | const url = 'http://localhost:3009/api/v1/snippets'; 27 | fetch(url, { 28 | method: 'POST', // *GET, POST, PUT, DELETE, etc. 29 | mode: 'cors', // no-cors, *cors, same-origin 30 | cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached 31 | credentials: 'same-origin', // include, *same-origin, omit 32 | headers: { 33 | 'Content-Type': 'application/json' 34 | }, 35 | redirect: 'follow', // manual, *follow, error 36 | referrerPolicy: 'no-referrer', // no-referrer, *client 37 | body: JSON.stringify({ 38 | menuItemId: data.menuItemId, 39 | saveCodeSnippet: data.selectionText, 40 | pageUrl: data.pageUrl 41 | }) // body data type must match "Content-Type" header 42 | }).then(d => console.log('response from server',d)) 43 | .catch(err => console.error(err)); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /chrome-extension/images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/chrome-extension/images/icon128.png -------------------------------------------------------------------------------- /chrome-extension/images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/chrome-extension/images/icon16.png -------------------------------------------------------------------------------- /chrome-extension/images/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/chrome-extension/images/icon256.png -------------------------------------------------------------------------------- /chrome-extension/images/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/chrome-extension/images/icon32.png -------------------------------------------------------------------------------- /chrome-extension/images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/chrome-extension/images/icon48.png -------------------------------------------------------------------------------- /chrome-extension/images/icon_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/chrome-extension/images/icon_big.png -------------------------------------------------------------------------------- /chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Coding snippet", 3 | "version": "1.0", 4 | "description": "Make coding great again!", 5 | "permissions": ["activeTab", "declarativeContent", "storage", "contextMenus"], 6 | "background": { 7 | "scripts": ["background.js"], 8 | "persistent": false 9 | }, 10 | "browser_action": { 11 | "default_popup": "popup.html" 12 | }, 13 | "icons": { 14 | "16": "images/icon16.png", 15 | "32": "images/icon32.png", 16 | "48": "images/icon48.png", 17 | "128": "images/icon128.png", 18 | "256": "images/icon256.png" 19 | }, 20 | "options_page": "options.html", 21 | "manifest_version": 2 22 | } -------------------------------------------------------------------------------- /chrome-extension/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 |
15 |
16 |
17 |

Choose a different background color!

18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /chrome-extension/options.js: -------------------------------------------------------------------------------- 1 | let page = document.getElementById('buttonDiv'); 2 | const kButtonColors = ['#3aa757', '#e8453c', '#f9bb2d', '#4688f1']; 3 | function constructOptions(kButtonColors) { 4 | for (let item of kButtonColors) { 5 | let button = document.createElement('button'); 6 | button.style.backgroundColor = item; 7 | button.addEventListener('click', function() { 8 | chrome.storage.sync.set({color: item}, function() { 9 | console.log('color is ' + item); 10 | }) 11 | }); 12 | page.appendChild(button); 13 | } 14 | } 15 | constructOptions(kButtonColors); -------------------------------------------------------------------------------- /chrome-extension/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 |
TEst header
14 | 15 | 16 | -------------------------------------------------------------------------------- /chrome-extension/popup.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | 'use strict'; 6 | 7 | let changeColor = document.getElementById('changeColor'); 8 | 9 | chrome.storage.sync.get('color', function(data) { 10 | changeColor.style.backgroundColor = data.color; 11 | changeColor.setAttribute('value', data.color); 12 | }); 13 | changeColor.onclick = function(element) { 14 | let color = element.target.value; 15 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 16 | chrome.tabs.executeScript( 17 | tabs[0].id, 18 | {code: 'document.body.style.backgroundColor = "' + color + '";'}); 19 | }); 20 | }; -------------------------------------------------------------------------------- /contribiuting.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | s 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # docker-compose.yml file 2 | 3 | version: '3.6' 4 | 5 | services: 6 | search_service: 7 | build: 8 | context: ./search-service 9 | dockerfile: Dockerfile-dev 10 | volumes: 11 | - type: bind 12 | source: ./search-service/ 13 | target: /usr/app 14 | depends_on: 15 | - elasticsearch 16 | links: 17 | - elasticsearch 18 | ports: 19 | - 3009:3009 20 | command: npm run install_and_run 21 | web_ui: 22 | build: 23 | context: ./web-ui 24 | dockerfile: Dockerfile-dev 25 | volumes: 26 | - type: bind 27 | source: ./web-ui/ 28 | target: /usr/app 29 | ports: 30 | - 3003:3003 31 | command: npm run start 32 | auth_service: 33 | build: 34 | context: ./auth-service 35 | dockerfile: Dockerfile-dev 36 | volumes: 37 | - type: bind 38 | source: ./auth-service/ 39 | target: /usr/app 40 | depends_on: 41 | - elasticsearch 42 | links: 43 | - elasticsearch 44 | ports: 45 | - 3010:3010 46 | command: npm run install_and_run 47 | elasticsearch: 48 | image: docker.elastic.co/elasticsearch/elasticsearch:7.5.1 49 | environment: 50 | - discovery.type=single-node 51 | ports: 52 | - 9300:9300 53 | - 9200:9200 54 | kibana: 55 | image: docker.elastic.co/kibana/kibana:7.5.2 56 | ports: 57 | - 5601:5601 58 | environment: 59 | ELASTICSEARCH_HOSTS: http://elasticsearch:9200 60 | rabbitmq: 61 | image: rabbitmq 62 | ports: 63 | - "15672:15672" 64 | - "5672:5672" 65 | mysql: 66 | image: mysql 67 | command: --default-authentication-plugin=mysql_native_password 68 | restart: always 69 | environment: 70 | MYSQL_ROOT_PASSWORD: w3lc0me! 71 | adminer: 72 | image: adminer 73 | restart: always 74 | ports: 75 | - 8080:8080 76 | 77 | -------------------------------------------------------------------------------- /elastic/README.md: -------------------------------------------------------------------------------- 1 | # Elastic 2 | 3 | ## Run locally with docker-compose 4 | 5 | `$ docker-compose up` 6 | 7 | ## Browse 8 | 9 | * go to : `http://localhost:9200/` for elasticsearch 10 | * go to : `http://localhost:5601/` for Kibana 11 | -------------------------------------------------------------------------------- /elastic/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # docker-compose.yml file 2 | 3 | version: '3.6' 4 | 5 | services: 6 | elasticsearch: 7 | image: docker.elastic.co/elasticsearch/elasticsearch:7.5.1 8 | container_name: elasticsearch 9 | environment: 10 | - discovery.type=single-node 11 | ports: 12 | - 9300:9300 13 | - 9200:9200 14 | kibana: 15 | image: docker.elastic.co/kibana/kibana:7.5.2 16 | container_name: kibana 17 | ports: 18 | - 5601:5601 19 | environment: 20 | ELASTICSEARCH_HOSTS: http://elasticsearch:9200 -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/logo.png -------------------------------------------------------------------------------- /mysql/README.md: -------------------------------------------------------------------------------- 1 | # Mysql DB 2 | 3 | ## Run locally with docker-compose 4 | 5 | `$ docker-compose up --build` 6 | 7 | ## Connect to web ui - Adminer 8 | 9 | `$ open localhost:8080` 10 | 11 | `$ user: root; password: check docker-compose.yml file` 12 | -------------------------------------------------------------------------------- /mysql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | 3 | services: 4 | mysql: 5 | image: mysql 6 | command: --default-authentication-plugin=mysql_native_password 7 | restart: always 8 | environment: 9 | MYSQL_ROOT_PASSWORD: w3lc0me! 10 | MYSQL_DATABASE: auth_service 11 | ports: 12 | - 3306:3306 13 | adminer: 14 | image: adminer 15 | restart: always 16 | ports: 17 | - 8080:8080 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "auth-service", 5 | "search-service", 6 | "text-service", 7 | "web-ui" 8 | ] 9 | } -------------------------------------------------------------------------------- /run_auth_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # dir=$(pwd) -v $dir:/app 3 | cd auth-service 4 | docker build -t auth_service . 5 | docker run -p 3010:3010 -t auth_service 6 | -------------------------------------------------------------------------------- /text-ml-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pytorch/pytorch 2 | WORKDIR /usr/app -------------------------------------------------------------------------------- /text-service/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: [ 9 | 'plugin:@typescript-eslint/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'prettier', 12 | 'prettier/@typescript-eslint', 13 | ], 14 | root: true, 15 | env: { 16 | node: true, 17 | jest: true, 18 | }, 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/no-explicit-any': 'off', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /text-service/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /text-service/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /text-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.16 2 | WORKDIR /usr/app 3 | COPY . . 4 | RUN yarn 5 | RUN yarn build 6 | CMD yarn start:prod -------------------------------------------------------------------------------- /text-service/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /text-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "text-service", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "Yohay Nahmany", 6 | "private": true, 7 | "license": "MIT", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json" 22 | }, 23 | "dependencies": { 24 | "@elastic/elasticsearch": "^7.6.1", 25 | "@nestjs/common": "^7.0.0", 26 | "@nestjs/config": "^0.4.1", 27 | "@nestjs/core": "^7.0.0", 28 | "@nestjs/elasticsearch": "^7.1.0", 29 | "@nestjs/platform-express": "^7.0.0", 30 | "@nestjs/swagger": "^4.5.5", 31 | "reflect-metadata": "^0.1.13", 32 | "rimraf": "^3.0.2", 33 | "rxjs": "^6.5.4" 34 | }, 35 | "devDependencies": { 36 | "@nestjs/cli": "^7.0.0", 37 | "@nestjs/schematics": "^7.0.0", 38 | "@nestjs/testing": "^7.0.0", 39 | "@types/express": "^4.17.3", 40 | "@types/jest": "25.1.4", 41 | "@types/node": "^13.9.1", 42 | "@types/supertest": "^2.0.8", 43 | "@typescript-eslint/eslint-plugin": "^2.23.0", 44 | "@typescript-eslint/parser": "^2.23.0", 45 | "eslint": "^6.8.0", 46 | "eslint-config-prettier": "^6.10.0", 47 | "eslint-plugin-import": "^2.20.1", 48 | "jest": "^25.1.0", 49 | "prettier": "^1.19.1", 50 | "supertest": "^4.0.2", 51 | "ts-jest": "25.2.1", 52 | "ts-loader": "^6.2.1", 53 | "ts-node": "^8.6.2", 54 | "tsconfig-paths": "^3.9.0", 55 | "typescript": "^3.7.4" 56 | }, 57 | "jest": { 58 | "moduleFileExtensions": [ 59 | "js", 60 | "json", 61 | "ts" 62 | ], 63 | "rootDir": "src", 64 | "testRegex": ".spec.ts$", 65 | "transform": { 66 | "^.+\\.(t|j)s$": "ts-jest" 67 | }, 68 | "coverageDirectory": "../coverage", 69 | "testEnvironment": "node" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /text-service/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return UP status from health"', () => { 19 | expect(appController.getHealthStatus().status).toBe('UP'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /text-service/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService, HealthCheck } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get('/health') 9 | getHealthStatus(): HealthCheck { 10 | return this.appService.checkHealth(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /text-service/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, HttpModule } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { SearchController } from './search/search.controller'; 5 | import { ConfigModule } from '@nestjs/config'; 6 | import Configuration from './config/configuration'; 7 | import { SearchService } from './search/search.service'; 8 | 9 | @Module({ 10 | imports: [ 11 | HttpModule, 12 | ConfigModule.forRoot({ 13 | load: [Configuration] 14 | }) 15 | ], 16 | controllers: [AppController, SearchController], 17 | providers: [AppService, SearchService], 18 | }) 19 | export class AppModule {} 20 | -------------------------------------------------------------------------------- /text-service/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | interface Service { 4 | name: string; 5 | version: string; 6 | status: string; 7 | } 8 | export interface HealthCheck { 9 | status: string; 10 | services: Service[]; 11 | } 12 | @Injectable() 13 | export class AppService { 14 | checkHealth(): HealthCheck { 15 | return { status: 'UP', services: [{ name: 'elastic', version: '7.5.1', status: 'UP' }]} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /text-service/src/config/configuration.ts: -------------------------------------------------------------------------------- 1 | interface IElastic { 2 | host: string; 3 | port: number; 4 | username?: string; 5 | password?: string; 6 | name?: string; 7 | } 8 | interface Auth { 9 | key: string; 10 | secret: string; 11 | callback: string; 12 | } 13 | interface Configuration { 14 | port: number, 15 | elastic: IElastic, 16 | twitter?: Auth 17 | } 18 | interface ConfigurationResult { 19 | development: Configuration; 20 | testing: Configuration; 21 | production?: Configuration; 22 | } 23 | const configuration: ConfigurationResult = { 24 | development: { 25 | port: parseInt(process.env.PORT, 10) || 3009, 26 | elastic: { 27 | host: 'localhost', 28 | port: 9200, 29 | username: 'root', 30 | password: 'w3lc0me!', 31 | name: 'auth_service', 32 | }, 33 | }, 34 | testing: { 35 | port: parseInt(process.env.PORT, 10) || 3009, 36 | elastic: { 37 | host: 'localhost', 38 | port: 9200, 39 | username: 'root', 40 | password: 'w3lc0me!', 41 | name: 'auth_service', 42 | } 43 | } 44 | }; 45 | export default (): Configuration => configuration[process.env.NODE_ENV] || configuration['development']; 46 | -------------------------------------------------------------------------------- /text-service/src/elastic/index.ts: -------------------------------------------------------------------------------- 1 | import { Client as NativeClient } from '@elastic/elasticsearch'; 2 | import Configuration from '../config/configuration'; 3 | const configuration = Configuration(); 4 | const convertArrayToObject = (array: any, key: string) => { 5 | const initialValue = {}; 6 | return array.reduce((obj: any, item: any) => { 7 | return { 8 | ...obj, 9 | [item[key]]: item, 10 | }; 11 | }, initialValue); 12 | }; 13 | const client = new NativeClient({ 14 | node: `http://${configuration.elastic.host}:${configuration.elastic.port}` 15 | }); 16 | 17 | 18 | const GetClient = () => { 19 | return client; 20 | } 21 | interface Body { 22 | properties: Record 23 | } 24 | interface Scheme { 25 | index: string, 26 | body: Body 27 | } 28 | const Snippets: Scheme = { 29 | index: 'snippets', 30 | body: { 31 | "properties": { 32 | "selectionText": { 33 | "type": "text" 34 | }, 35 | "pageUrl": { 36 | "type": "text" 37 | } 38 | } 39 | } 40 | } 41 | const Test: Scheme = { 42 | index: 'test', 43 | body: Object.create({}) 44 | } 45 | const Schemes: Array = [Snippets, Test]; 46 | const SchemesNameToIndices = convertArrayToObject(Schemes, 'index'); 47 | 48 | const init = async (): Promise => { 49 | try { 50 | const client = GetClient(); 51 | const indices = Object.keys(SchemesNameToIndices); 52 | 53 | await Promise.all(indices.map(async index => { 54 | const { body: isIndexExist } = await client.indices.exists({ index }); 55 | if (!isIndexExist) { 56 | await client.indices.create({ index }) 57 | await client.indices.putMapping(SchemesNameToIndices[index]) 58 | } 59 | })); 60 | } 61 | catch(err) { 62 | console.log(err); 63 | throw err; 64 | } 65 | } 66 | 67 | export default { GetClient, Schemes, SchemesNameToIndices, init }; 68 | -------------------------------------------------------------------------------- /text-service/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 3 | import { AppModule } from './app.module'; 4 | import configuration from './config/configuration'; 5 | 6 | 7 | async function bootstrap() { 8 | const app = await NestFactory.create(AppModule); 9 | const Configuration = configuration(); 10 | const options = new DocumentBuilder() 11 | .setTitle('Text Service') 12 | .setDescription('Text service should provide all apis for storing and retrieve text data for bb project') 13 | .setVersion('1.0') 14 | .addTag('text') 15 | .build(); 16 | const document = SwaggerModule.createDocument(app, options); 17 | SwaggerModule.setup('api', app, document); 18 | app.enableCors(); 19 | await app.listen(Configuration.port); 20 | } 21 | bootstrap(); 22 | -------------------------------------------------------------------------------- /text-service/src/search/search.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { SearchController } from './search.controller'; 3 | import { SearchService } from './search.service'; 4 | 5 | describe('Search Controller', () => { 6 | let controller: SearchController; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | controllers: [SearchController], 11 | providers: [SearchService] 12 | }).compile(); 13 | 14 | controller = module.get(SearchController); 15 | }); 16 | 17 | it('should be defined', () => { 18 | expect(controller).toBeDefined(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /text-service/src/search/search.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body } from '@nestjs/common'; 2 | import { SearchService } from './search.service'; 3 | 4 | import Elastic from '../elastic'; 5 | import { ApiResponse, RequestParams } from '@elastic/elasticsearch' 6 | const SNIPPET_INDEX = Elastic.SchemesNameToIndices['snippets'].index; 7 | 8 | interface Snippet { 9 | pageUrl: string, 10 | selectionText: string, 11 | menuItemId: string 12 | } 13 | 14 | @Controller('search') 15 | export class SearchController { 16 | constructor(private service: SearchService) { } 17 | @Get('/') 18 | async getText(): Promise { 19 | try { 20 | const params1: RequestParams.Search = { 21 | index: SNIPPET_INDEX 22 | } 23 | const client = Elastic.GetClient(); 24 | const result: ApiResponse = await client.search(params1); 25 | return result.body.hits.hits; 26 | } 27 | catch (err) { 28 | return err; 29 | } 30 | 31 | } 32 | @Post('/') 33 | // @UsePipes(new JoiValidationPipe(createWebRegisterSchema())) 34 | async createText(@Body() data: Snippet): Promise { 35 | try { 36 | const client = Elastic.GetClient(); 37 | const doc: RequestParams.Index = { 38 | index: SNIPPET_INDEX, 39 | refresh: "true", 40 | body: { 41 | selectionText: data.selectionText, 42 | pageUrl: data.pageUrl 43 | } 44 | } 45 | await client.index(doc); 46 | return 'Created successfully'; 47 | } 48 | catch (err) { 49 | return err; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /text-service/src/search/search.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { SearchService } from './search.service'; 3 | import { SearchController } from './search.controller'; 4 | 5 | @Module({ 6 | providers: [SearchService], 7 | controllers: [SearchController], 8 | exports: [SearchService] 9 | }) 10 | export class SearchModule {} 11 | -------------------------------------------------------------------------------- /text-service/src/search/search.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | @Injectable() 3 | export class SearchService { 4 | getTest() { 5 | return 'another text'; 6 | } 7 | 8 | } -------------------------------------------------------------------------------- /text-service/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /text-service/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /text-service/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /text-service/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noErrorTruncation": true, 4 | "noImplicitAny": true, 5 | "suppressImplicitAnyIndexErrors": false, 6 | "strictNullChecks": true, 7 | "noFallthroughCasesInSwitch": true, 8 | "noImplicitReturns": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": false, 11 | "noImplicitThis": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "skipLibCheck": true, 14 | "removeComments": false, 15 | "preserveConstEnums": true, 16 | "sourceMap": true, 17 | "experimentalDecorators": true, 18 | "emitDecoratorMetadata": true, 19 | "target": "es2015", 20 | "module": "commonjs", 21 | "jsx": "react", 22 | "lib": [ 23 | "DOM", 24 | "ESnext" 25 | ], 26 | "baseUrl": ".", 27 | "outDir": "dist", 28 | "rootDir": ".", 29 | "paths": { 30 | "~/*": ["*/*"] 31 | } 32 | }, 33 | "exclude": [ 34 | "node_modules", 35 | "dist" 36 | ] 37 | } -------------------------------------------------------------------------------- /type-test/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | var index_1 = require("@brain-backup/types/index"); 4 | var q = { 5 | a: 'what is the time', 6 | q: ' dont know', 7 | date: new Date() 8 | }; 9 | console.log(index_1.mlqa(q)); 10 | -------------------------------------------------------------------------------- /type-test/index.ts: -------------------------------------------------------------------------------- 1 | import { mlqa, Question } from '@brain-backup/types/index'; 2 | 3 | const q: Question = { 4 | a: 'what is the time', 5 | q: ' dont know', 6 | date: new Date() 7 | } 8 | console.log(mlqa(q)); -------------------------------------------------------------------------------- /type-test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "type-test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@brain-backup/types": { 8 | "version": "0.0.1", 9 | "resolved": "https://registry.npmjs.org/@brain-backup/types/-/types-0.0.1.tgz", 10 | "integrity": "sha512-KBXdsxv+fQWPx6YXb8ssjTGeT0ayLEjYnP7Esjh/mWfdyfHGPYVe/vAnhbWBNIHAcnUabkANdFcq4Taf5TctVg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /type-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "type-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@brain-backup/types": "0.0.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /types/bb-tests.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/types/bb-tests.ts -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface Question { 2 | q?: string; 3 | a?: string; 4 | date: Date; 5 | } 6 | -------------------------------------------------------------------------------- /types/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | function mlqa(q) { 4 | return 9; 5 | } 6 | exports.mlqa = mlqa; 7 | -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | export interface Question { 2 | q?: string; 3 | a?: string; 4 | date: Date; 5 | } 6 | 7 | export function mlqa(q: Question) : number { 8 | return 9; 9 | } 10 | -------------------------------------------------------------------------------- /types/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bb/types", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@brain-backup/types", 3 | "version": "0.0.2", 4 | "description": "types for Brain backup(bb) organization.", 5 | "main": "index.js", 6 | "typings": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Yohay Nahmany", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /types/package.json.old: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bb/types", 3 | "author": "Yohay Nahmany", 4 | "version": "1.0.0", 5 | "types": "./index.d.ts" 6 | } -------------------------------------------------------------------------------- /types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es6" 7 | ], 8 | "noImplicitAny": true, 9 | "noImplicitThis": true, 10 | "strictNullChecks": true, 11 | "strictFunctionTypes": true, 12 | "types": [], 13 | "noEmit": true, 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "files": [ 17 | "index.d.ts" 18 | ] 19 | } -------------------------------------------------------------------------------- /types/tslint.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/types/tslint.json -------------------------------------------------------------------------------- /web-ui/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_STORE 3 | node_modules 4 | .module-cache 5 | *.log* 6 | build 7 | dist -------------------------------------------------------------------------------- /web-ui/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "semi": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "bracketSpacing": true, 7 | "singleQuote": true 8 | } 9 | -------------------------------------------------------------------------------- /web-ui/.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "allowSyntheticDefaultImports": true, 5 | "module": "es2015", 6 | "target": "es5", 7 | "lib": ["es6", "dom"], 8 | "sourceMap": true, 9 | "allowJs": false, 10 | "jsx": "react", 11 | "moduleResolution": "node", 12 | "rootDir": "../", 13 | "outDir": "dist", 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": true, 18 | "declaration": true 19 | }, 20 | "include": [ 21 | "src/**/*" 22 | ], 23 | "exclude": [ 24 | "node_modules", 25 | "build", 26 | "dist", 27 | "scripts", 28 | "acceptance-tests", 29 | "webpack", 30 | "jest", 31 | "src/setupTests.ts", 32 | "**/*/*.test.ts", 33 | "examples" 34 | ] 35 | } -------------------------------------------------------------------------------- /web-ui/.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const SRC_PATH = path.join(__dirname, '../src'); 3 | // const STORIES_PATH = path.join(__dirname, '../stories'); 4 | //dont need stories path if you have your stories inside your //components folder 5 | module.exports = ({config}) => { 6 | config.module.rules.push({ 7 | test: /\.(ts|tsx)$/, 8 | include: [SRC_PATH], 9 | use: [ 10 | { 11 | loader: require.resolve('awesome-typescript-loader'), 12 | options: { 13 | configFileName: './.storybook/tsconfig.json' 14 | } 15 | }, 16 | { loader: require.resolve('react-docgen-typescript-loader') } 17 | ] 18 | }); 19 | config.resolve.extensions.push('.ts', '.tsx'); 20 | return config; 21 | }; -------------------------------------------------------------------------------- /web-ui/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13.6 2 | WORKDIR /usr/app 3 | COPY . . 4 | RUN npm cache clean --force 5 | RUN rm package-lock.json 6 | RUN npm install 7 | CMD npm run start 8 | -------------------------------------------------------------------------------- /web-ui/Dockerfile-dev: -------------------------------------------------------------------------------- 1 | FROM node:13.6 2 | WORKDIR /usr/app 3 | -------------------------------------------------------------------------------- /web-ui/README.md: -------------------------------------------------------------------------------- 1 | # Brain backup - web ui 2 | 3 | ``` 4 | $ yarn start 5 | ``` 6 | 7 | ``` 8 | $ yarn install 9 | ``` 10 | 11 | ``` 12 | $ yarn run storybook 13 | ``` 14 | -------------------------------------------------------------------------------- /web-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-ui", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Web application for brain backup project", 6 | "author": "Yohay Nahmany", 7 | "main": "index.js", 8 | "scripts": { 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "start": "webpack-dev-server --mode development --host 0.0.0.0 --hot --progress --colors --port 3003 --open", 11 | "build": "webpack -p --progress --colors", 12 | "prettier": "prettier --write \"src/**/*.{ts,tsx,css}\"", 13 | "storybook": "start-storybook -p 9001 -c .storybook" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.2.2", 18 | "@storybook/react": "^5.3.12", 19 | "@types/axios": "^0.14.0", 20 | "@types/classnames": "^2.2.7", 21 | "@types/node": "^10.12.18", 22 | "@types/react": "^16.7.20", 23 | "@types/react-dom": "^16.0.11", 24 | "@types/react-router": "^4.4.3", 25 | "@types/webpack": "^4.4.23", 26 | "awesome-typescript-loader": "^5.2.1", 27 | "babel-loader": "^8.0.5", 28 | "css-loader": "^2.1.0", 29 | "file-loader": "^3.0.1", 30 | "html-loader": "^1.0.0-alpha.0", 31 | "html-webpack-plugin": "^3.2.0", 32 | "mini-css-extract-plugin": "^0.5.0", 33 | "mobx-react-devtools": "^6.0.3", 34 | "postcss": "^8.2.10", 35 | "postcss-browser-reporter": "^0.5.0", 36 | "postcss-import": "^12.0.1", 37 | "postcss-loader": "^3.0.0", 38 | "postcss-preset-env": "^6.5.0", 39 | "postcss-reporter": "^6.0.1", 40 | "postcss-url": "^8.0.0", 41 | "prettier": "^1.16.0", 42 | "react-docgen-typescript-loader": "^3.6.0", 43 | "react-hot-loader": "^4.6.3", 44 | "storybook": "^5.3.12", 45 | "style-loader": "^0.23.1", 46 | "ts-loader": "^5.3.3", 47 | "typescript": "^3.2.4", 48 | "url-loader": "^1.1.2", 49 | "webpack": "^4.29.0", 50 | "webpack-cleanup-plugin": "^0.5.1", 51 | "webpack-cli": "^3.2.1", 52 | "webpack-dev-server": "^3.1.14", 53 | "webpack-hot-middleware": "^2.24.3" 54 | }, 55 | "dependencies": { 56 | "@material-ui/core": "~4.11.4", 57 | "@material-ui/icons": "~4.11.2", 58 | "axios": "^1.7.8", 59 | "classnames": "~2.3.1", 60 | "mobx": "~5.15.7", 61 | "mobx-react": "~5.4.3", 62 | "mobx-react-router": "~4.0.5", 63 | "react": "~16.14.0", 64 | "react-dom": "~16.14.0", 65 | "react-icons": "^3.11.0", 66 | "react-router": "~4.3.1" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /web-ui/semantic.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "semantic/", 3 | "paths": { 4 | "source": { 5 | "config": "src/theme.config", 6 | "definitions": "src/definitions/", 7 | "site": "src/site/", 8 | "themes": "src/themes/" 9 | }, 10 | "output": { 11 | "packaged": "dist/", 12 | "uncompressed": "dist/components/", 13 | "compressed": "dist/components/", 14 | "themes": "dist/themes/" 15 | }, 16 | "clean": "dist/" 17 | }, 18 | "permission": false, 19 | "autoInstall": false, 20 | "rtl": false, 21 | "version": "2.4.2" 22 | } -------------------------------------------------------------------------------- /web-ui/src/app/components/CustomInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { FormControl, InputLabel, Input } from '@material-ui/core'; 3 | 4 | interface IUsername { 5 | onChange: React.ChangeEventHandler, 6 | label: string, 7 | fullWidth?: boolean, 8 | style?: object 9 | } 10 | const CustomInput: React.FunctionComponent = ({ label, onChange, ...props }) => { 11 | return ( 12 | 13 | {label} 14 | 15 | 16 | ) 17 | } 18 | export default CustomInput; 19 | -------------------------------------------------------------------------------- /web-ui/src/app/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as classNames from 'classnames'; 3 | import * as style from './style.css'; 4 | import { 5 | TodoFilter, 6 | TODO_FILTER_TITLES, 7 | TODO_FILTER_TYPES 8 | } from 'app/constants'; 9 | 10 | export interface FooterProps { 11 | filter: TodoFilter; 12 | activeCount: number; 13 | completedCount: number; 14 | onChangeFilter: (filter: TodoFilter) => any; 15 | onClearCompleted: () => any; 16 | } 17 | 18 | export interface FooterState { 19 | /* empty */ 20 | } 21 | 22 | export class Footer extends React.Component { 23 | renderTodoCount() { 24 | const { activeCount } = this.props; 25 | const itemWord = activeCount === 1 ? 'item' : 'items'; 26 | 27 | return ( 28 | 29 | {activeCount || 'No'} {itemWord} left 30 | 31 | ); 32 | } 33 | 34 | renderFilterLink(filter: TodoFilter) { 35 | const title = TODO_FILTER_TITLES[filter]; 36 | const { filter: selectedFilter, onChangeFilter } = this.props; 37 | const className = classNames({ 38 | [style.selected]: filter === selectedFilter 39 | }); 40 | 41 | return ( 42 | onChangeFilter(filter)} 46 | > 47 | {title} 48 | 49 | ); 50 | } 51 | 52 | renderClearButton() { 53 | const { completedCount, onClearCompleted } = this.props; 54 | if (completedCount > 0) { 55 | return ( 56 | 18 | ) 19 | } 20 | export default FacebookButton; 21 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/components/GithubButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Button } from '@material-ui/core'; 3 | import { FaGithub } from 'react-icons/fa'; 4 | //TODO: export it and reuse in other buttons 5 | interface IButton { 6 | onClick: React.ChangeEventHandler 7 | } 8 | const GithubButton: React.FunctionComponent = ({ onClick, ...props }) => { 9 | return ( 10 | 18 | ) 19 | } 20 | export default GithubButton; 21 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/components/Login.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Button } from '@material-ui/core'; 3 | import CustomInput from 'app/components/CustomInput'; 4 | import Password from 'app/components/Password'; 5 | import axios, { AxiosResponse } from 'axios'; 6 | 7 | interface ILogin {} 8 | interface IForm { 9 | mailAddress: string, 10 | password: string 11 | }; 12 | const Login: React.FunctionComponent = ({ }) => { 13 | const initialState: IForm = { 14 | mailAddress: '', 15 | password: 's' 16 | }; 17 | const [form, setForm] = React.useState(initialState); 18 | const onSubmit = () => { 19 | axios.post('http://localhost:3010/auth/login', form).then((data: AxiosResponse) => sessionStorage.setItem('jwt', data.data)); 20 | } 21 | return ( 22 |
23 | setForm({ ...form, mailAddress: e.target.value })} /> 24 | setForm({ ...form, password: e.target.value })}/> 25 | 32 |
33 | ) 34 | }; 35 | export default Login; 36 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/components/Register.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Button } from '@material-ui/core'; 3 | import Password from 'app/components/Password'; 4 | import CustomInput from 'app/components/CustomInput'; 5 | import axios from 'axios'; 6 | 7 | interface IRegister { 8 | 9 | } 10 | interface IForm { 11 | firstName: string, 12 | lastName: string, 13 | mailAddress: string, 14 | password: string 15 | }; 16 | const IRegister: React.FunctionComponent = ({ }) => { 17 | const initialState: IForm = { 18 | firstName: '', 19 | lastName: '', 20 | mailAddress: '', 21 | password: '' 22 | }; 23 | const [form, setForm] = React.useState(initialState); 24 | // const onSubmit = () => { 25 | // fetch('http://localhost:3010/users', 26 | // { 27 | // method: 'POST', 28 | // body: JSON.stringify(form), 29 | // headers: { 30 | // 'Access-Control-Allow-Origin': '*' 31 | // } 32 | // }) 33 | // .then(data => console.log(data)) 34 | // .catch(err => console.error(err)); 35 | // } 36 | 37 | const onSubmit = () => { 38 | // , { headers: { 'Access-Control-Allow-Origin':'*',"Access-Control-Allow-Methods":"GET,PUT,POST,DELETE","Access-Control-Allow-Headers":"Content-Type","X-Frame-Options":"ALLOWALL" }} 39 | axios.post('http://localhost:3010/users', form).then(data => console.log(data)); 40 | // fetch('http://localhost:3010/users/al2l').then(resp => console.log(resp)); 41 | } 42 | return ( 43 |
44 | {/* TODO: add header */} 45 |
46 | setForm({ ...form, firstName: e.target.value })} /> 47 | setForm({ ...form, lastName: e.target.value })} /> 48 |
49 | setForm({ ...form, mailAddress: e.target.value })} /> 50 | setForm({ ...form, password: e.target.value })}/> 51 | 58 |
59 | ) 60 | }; 61 | export default IRegister; 62 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/components/TabsWrapper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import { Tab, Tabs } from '@material-ui/core'; 4 | 5 | const useStyles = makeStyles({ 6 | tabsRoot: { 7 | }, 8 | tabsIndicator: { 9 | backgroundColor: '#3e90ff' 10 | }, 11 | tabRoot: { 12 | 'color': '#9191a9', 13 | 'textTransform': 'initial', 14 | 'fontSize': '18px', 15 | '&:hover': { 16 | color: '#3e90ff', 17 | opacity: 1 18 | }, 19 | '&tabSelected': { 20 | color: '#3e90ff' 21 | }, 22 | '&:focus': { 23 | color: '#3e90ff' 24 | } 25 | } 26 | }); 27 | // TODO: enable this and fix the Dispatch error 28 | // interface ITabs { 29 | // onChange: Dispatch>, 30 | // activeTab: number 31 | // } 32 | // const TabsWrapper: React.FunctionComponent = ({ }) => { 33 | // https://github.com/reduxjs/redux-thunk/issues/242 34 | const TabsWrapper = ({ onChange, activeTab }) => { 35 | const classes = useStyles(); 36 | return ( 37 | onChange(value)} 40 | classes={{ root: classes.tabsRoot, indicator: classes.tabsIndicator }} 41 | > 42 | 47 | 52 | 53 | ) 54 | 55 | }; 56 | export default TabsWrapper; 57 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/components/TwitterButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Button } from '@material-ui/core'; 3 | import { FaTwitter } from 'react-icons/fa'; 4 | 5 | interface IButton { 6 | onClick(): void 7 | } 8 | const TwitterButton: React.FunctionComponent = ({ onClick, ...props }) => { 9 | return ( 10 | 19 | ) 20 | } 21 | export default TwitterButton; 22 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/constants/index.tsx: -------------------------------------------------------------------------------- 1 | export const TABS = { 2 | LOGIN: 0, 3 | REGISTER: 1 4 | }; -------------------------------------------------------------------------------- /web-ui/src/app/routes/authentication/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import { Grid, Divider, Card, CardContent } from '@material-ui/core'; 4 | import TwitterButton from './components/TwitterButton'; 5 | import GithubButton from './components/GithubButton'; 6 | import FacebookButton from './components/FacebookButton'; 7 | import TabsWrapper from './components/TabsWrapper'; 8 | import { TABS } from './constants'; 9 | import Login from './components/Login'; 10 | import Register from './components/Register'; 11 | 12 | const useStyles = makeStyles({ 13 | root: { 14 | position: 'fixed', 15 | left: '20%', 16 | top: '30%', 17 | maxWidth: 900, 18 | }, 19 | bullet: { 20 | display: 'inline-block', 21 | margin: '0 2px', 22 | transform: 'scale(0.8)', 23 | }, 24 | title: { 25 | fontSize: 14, 26 | }, 27 | pos: { 28 | marginBottom: 12, 29 | }, 30 | }); 31 | interface IAuth { 32 | 33 | } 34 | const Authentication: React.FunctionComponent = ({ }) => { 35 | const [activeTab, setActiveTab] = React.useState(TABS.LOGIN); 36 | // export default function Authentication() { 37 | const classes = useStyles(); 38 | const viewActiveTab = { 39 | [TABS.LOGIN]: , 40 | [TABS.REGISTER]: 41 | }; 42 | const onTwitterClick = () => { 43 | fetch('https://api.twitter.com/oauth/request_token', 44 | { 45 | method: 'POST' 46 | }) 47 | .then(data => console.log(data)) 48 | .catch(err => console.error(err)); 49 | } 50 | return ( 51 | 52 | 53 | 54 | 55 |
56 | 57 | console.log('on click')}/> 58 | console.log('on click')}/> 59 | 60 |
61 |
62 | 63 | 64 | 65 | 66 | 67 | { viewActiveTab[activeTab] } 68 | 69 |
70 |
71 |
72 | ); 73 | } 74 | export default Authentication; 75 | -------------------------------------------------------------------------------- /web-ui/src/app/routes/main/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/web-ui/src/app/routes/main/index.ts -------------------------------------------------------------------------------- /web-ui/src/app/stores/RouterStore.ts: -------------------------------------------------------------------------------- 1 | import { History } from 'history'; 2 | import { 3 | RouterStore as BaseRouterStore, 4 | syncHistoryWithStore 5 | } from 'mobx-react-router'; 6 | 7 | export class RouterStore extends BaseRouterStore { 8 | constructor(history?: History) { 9 | super(); 10 | if (history) { 11 | this.history = syncHistoryWithStore(history, this); 12 | } 13 | } 14 | } 15 | 16 | export default RouterStore; 17 | -------------------------------------------------------------------------------- /web-ui/src/app/stores/TodoStore.ts: -------------------------------------------------------------------------------- 1 | import { observable, computed, action } from 'mobx'; 2 | import { TodoModel } from 'app/models'; 3 | 4 | export class TodoStore { 5 | constructor(fixtures: TodoModel[]) { 6 | this.todos = fixtures; 7 | } 8 | 9 | @observable public todos: Array; 10 | 11 | @computed 12 | get activeTodos() { 13 | return this.todos.filter((todo) => !todo.completed); 14 | } 15 | 16 | @computed 17 | get completedTodos() { 18 | return this.todos.filter((todo) => todo.completed); 19 | } 20 | 21 | @action 22 | addTodo = (item: Partial): void => { 23 | this.todos.push(new TodoModel(item.text, item.completed)); 24 | }; 25 | 26 | @action 27 | editTodo = (id: number, data: Partial): void => { 28 | this.todos = this.todos.map((todo) => { 29 | if (todo.id === id) { 30 | if (typeof data.completed == 'boolean') { 31 | todo.completed = data.completed; 32 | } 33 | if (typeof data.text == 'string') { 34 | todo.text = data.text; 35 | } 36 | } 37 | return todo; 38 | }); 39 | }; 40 | 41 | @action 42 | deleteTodo = (id: number): void => { 43 | this.todos = this.todos.filter((todo) => todo.id !== id); 44 | }; 45 | 46 | @action 47 | completeAll = (): void => { 48 | this.todos = this.todos.map((todo) => ({ ...todo, completed: true })); 49 | }; 50 | 51 | @action 52 | clearCompleted = (): void => { 53 | this.todos = this.todos.filter((todo) => !todo.completed); 54 | }; 55 | } 56 | 57 | export default TodoStore; 58 | -------------------------------------------------------------------------------- /web-ui/src/app/stores/createStore.ts: -------------------------------------------------------------------------------- 1 | import { History } from 'history'; 2 | import { TodoModel } from 'app/models'; 3 | import { TodoStore } from './TodoStore'; 4 | import { RouterStore } from './RouterStore'; 5 | import { STORE_TODO, STORE_ROUTER } from 'app/constants'; 6 | 7 | export function createStores(history: History, defaultTodos?: TodoModel[]) { 8 | const todoStore = new TodoStore(defaultTodos); 9 | const routerStore = new RouterStore(history); 10 | return { 11 | [STORE_TODO]: todoStore, 12 | [STORE_ROUTER]: routerStore 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /web-ui/src/app/stores/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TodoStore'; 2 | export * from './RouterStore'; 3 | export * from './createStore'; 4 | -------------------------------------------------------------------------------- /web-ui/src/app/styledComponents/Sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Sidebar() { 4 | return ( 5 |
TESTing side bar with storybook
6 | ) 7 | } 8 | export default Sidebar; 9 | -------------------------------------------------------------------------------- /web-ui/src/app/styledComponents/Sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | function Sidebar({}) { 4 | return ( 5 |
6 | Test asd 7 |
8 | ) 9 | } 10 | export default Sidebar; 11 | -------------------------------------------------------------------------------- /web-ui/src/app/styledComponents/Sidebar/sidebar.story.js: -------------------------------------------------------------------------------- 1 | // // import * as React from 'react'; 2 | // // import Sidebar from './index'; 3 | import Sidebar from './Sidebar'; 4 | 5 | // export default { title: 'Button' }; 6 | 7 | // // export const withEmoji = () => Sidebar; 8 | // // export const story = () => Sidebar; 9 | // import React from 'react'; 10 | import { storiesOf } from '@storybook/react'; 11 | // import Toggle from './index'; 12 | // import { boolean, text } from '@storybook/addon-knobs'; 13 | 14 | const stories = storiesOf('Toggle', module); 15 | 16 | stories.add('On or off', () => { 17 | return ( 18 | 19 | ); 20 | }); -------------------------------------------------------------------------------- /web-ui/src/app/theme/dark.ts: -------------------------------------------------------------------------------- 1 | import { createMuiTheme } from "@material-ui/core"; 2 | 3 | // #212121 4 | // #323232 5 | // #0d7377 6 | // #14ffec 7 | const theme = createMuiTheme({ 8 | palette: { 9 | primary: { 10 | main: '#212121' 11 | }, 12 | secondary: { 13 | light: '#323232', 14 | main: '#0d7377', 15 | contrastText: '#14ffec', 16 | }, 17 | contrastThreshold: 3, 18 | tonalOffset: 0.2 19 | }, 20 | typography: { 21 | 22 | }, 23 | overrides: { 24 | // MuiListItemIcon: { 25 | // root: { 26 | // color: 'white' 27 | // } 28 | // }, 29 | // MuiDivider: { 30 | // root: { 31 | // backgroundColor: 'white' 32 | // } 33 | // } 34 | } 35 | }); 36 | export default theme; 37 | // 7045af 38 | // e14594 -------------------------------------------------------------------------------- /web-ui/src/app/theme/light.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/web-ui/src/app/theme/light.ts -------------------------------------------------------------------------------- /web-ui/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrainBackup/bb8/1562da57815536d5da7a48df37e5e018e69b0413/web-ui/src/assets/favicon.ico -------------------------------------------------------------------------------- /web-ui/src/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Brain Backup 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /web-ui/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { Provider } from 'mobx-react'; 4 | import { TodoModel } from 'app/models'; 5 | import { createStores } from 'app/stores'; 6 | import { App } from 'app'; 7 | import history from './app/history'; 8 | 9 | // default fixtures for TodoStore 10 | const defaultTodos = [ 11 | new TodoModel('Use Mobx'), 12 | new TodoModel('Use React'), 13 | new TodoModel('Make ui works', true) 14 | ]; 15 | 16 | // prepare MobX stores 17 | const rootStore = createStores(history, defaultTodos); 18 | 19 | // render react DOM 20 | ReactDOM.render( 21 | 22 | 23 | , 24 | document.getElementById('root') 25 | ); 26 | -------------------------------------------------------------------------------- /web-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "target": "es5", 5 | "jsx": "react", 6 | "module": "es6", 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "declaration": false, 11 | "noImplicitAny": false, 12 | "noImplicitReturns": false, 13 | "noUnusedLocals": true, 14 | "removeComments": true, 15 | "strictNullChecks": false, 16 | "outDir": "build", 17 | "lib": ["es6", "es7", "dom"], 18 | "baseUrl": "src", 19 | "paths": { 20 | "app/*": ["./app/*"] 21 | } 22 | }, 23 | "exclude": ["dist", "build", "node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /web-ui/types/global.d.ts: -------------------------------------------------------------------------------- 1 | /** Global definitions for developement **/ 2 | 3 | // for style loader 4 | declare module '*.css' { 5 | const styles: any; 6 | export = styles; 7 | } 8 | -------------------------------------------------------------------------------- /web-ui/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | 4 | // variables 5 | var isProduction = 6 | process.argv.indexOf('-p') >= 0 || process.env.NODE_ENV === 'production'; 7 | var sourcePath = path.join(__dirname, './src'); 8 | var outPath = path.join(__dirname, './build'); 9 | 10 | // plugins 11 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 12 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 13 | var WebpackCleanupPlugin = require('webpack-cleanup-plugin'); 14 | 15 | 16 | module.exports = { 17 | context: sourcePath, 18 | entry: { 19 | app: './main.tsx' 20 | }, 21 | output: { 22 | path: outPath, 23 | filename: isProduction ? '[contenthash].js' : '[hash].js', 24 | chunkFilename: isProduction ? '[name].[contenthash].js' : '[name].[hash].js' 25 | }, 26 | target: 'web', 27 | resolve: { 28 | extensions: ['.js', '.ts', '.tsx'], 29 | // Fix webpack's default behavior to not load packages with jsnext:main module 30 | // (jsnext:main directs not usually distributable es6 format, but es6 sources) 31 | mainFields: ['module', 'browser', 'main'], 32 | modules: [path.resolve(__dirname, '../node_modules')], 33 | alias: { 34 | app: path.resolve(__dirname, 'src/app/'), 35 | } 36 | }, 37 | module: { 38 | rules: [ 39 | // .ts, .tsx 40 | { 41 | test: /\.tsx?$/, 42 | use: [ 43 | !isProduction && { 44 | loader: 'babel-loader', 45 | options: { plugins: ['react-hot-loader/babel'] } 46 | }, 47 | 'ts-loader' 48 | ].filter(Boolean) 49 | }, 50 | // css 51 | { 52 | test: /\.css$/, 53 | use: [ 54 | isProduction ? MiniCssExtractPlugin.loader : 'style-loader', 55 | { 56 | loader: 'css-loader', 57 | query: { 58 | modules: true, 59 | sourceMap: !isProduction, 60 | importLoaders: 1, 61 | localIdentName: isProduction 62 | ? '[hash:base64:5]' 63 | : '[local]__[hash:base64:5]' 64 | } 65 | }, 66 | { 67 | loader: 'postcss-loader', 68 | options: { 69 | ident: 'postcss', 70 | plugins: [ 71 | require('postcss-import')({ addDependencyTo: webpack }), 72 | require('postcss-url')(), 73 | require('postcss-preset-env')({ 74 | /* use stage 2 features (defaults) */ 75 | stage: 2 76 | }), 77 | require('postcss-reporter')(), 78 | require('postcss-browser-reporter')({ 79 | disabled: isProduction 80 | }) 81 | ] 82 | } 83 | } 84 | ] 85 | }, 86 | // static assets 87 | { test: /\.html$/, use: 'html-loader' }, 88 | { test: /\.(a?png|svg)$/, use: 'url-loader?limit=10000' }, 89 | { 90 | test: /\.(jpe?g|gif|bmp|mp3|mp4|ogg|wav|eot|ttf|woff|woff2)$/, 91 | use: 'file-loader' 92 | } 93 | ] 94 | }, 95 | optimization: { 96 | splitChunks: { 97 | name: true, 98 | cacheGroups: { 99 | commons: { 100 | chunks: 'initial', 101 | minChunks: 2 102 | }, 103 | vendors: { 104 | test: /[\\/]node_modules[\\/]/, 105 | chunks: 'all', 106 | priority: -10, 107 | filename: isProduction ? 'vendor.[contenthash].js' : 'vendor.[hash].js' 108 | } 109 | } 110 | }, 111 | runtimeChunk: true 112 | }, 113 | plugins: [ 114 | new webpack.EnvironmentPlugin({ 115 | NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined 116 | DEBUG: false 117 | }), 118 | new WebpackCleanupPlugin(), 119 | new MiniCssExtractPlugin({ 120 | filename: isProduction ? '[contenthash].css' : '[hash].css', 121 | disable: !isProduction 122 | }), 123 | new HtmlWebpackPlugin({ 124 | template: 'assets/index.html' 125 | }) 126 | ], 127 | devServer: { 128 | contentBase: sourcePath, 129 | hot: true, 130 | inline: true, 131 | historyApiFallback: { 132 | disableDotRule: true 133 | }, 134 | stats: 'minimal', 135 | clientLogLevel: 'warning' 136 | }, 137 | // https://webpack.js.org/configuration/devtool/ 138 | devtool: isProduction ? 'hidden-source-map' : 'cheap-module-eval-source-map', 139 | node: { 140 | // workaround for webpack-dev-server issue 141 | // https://github.com/webpack/webpack-dev-server/issues/60#issuecomment-103411179 142 | fs: 'empty', 143 | net: 'empty' 144 | } 145 | }; 146 | --------------------------------------------------------------------------------