Full Code of linnovate/mean for AI

master 5c12e1303c91 cached
82 files
58.8 KB
16.9k tokens
60 symbols
1 requests
Download .txt
Repository: linnovate/mean
Branch: master
Commit: 5c12e1303c91
Files: 82
Total size: 58.8 KB

Directory structure:
gitextract_bnkb_igt/

├── .dockerignore
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierrc
├── CNAME
├── Dockerfile
├── README.md
├── _config.yml
├── _layouts/
│   └── default.html
├── angular.json
├── assets/
│   └── css/
│       └── style.scss
├── docker-compose.yml
├── karma.conf.js
├── lint-staged.config.mjs
├── package.json
├── protractor.conf.js
├── server/
│   ├── config/
│   │   ├── config.js
│   │   ├── express.js
│   │   ├── mongoose.js
│   │   ├── passport.js
│   │   └── swagger.json
│   ├── controllers/
│   │   ├── auth.controller.js
│   │   └── user.controller.js
│   ├── index.js
│   ├── middleware/
│   │   └── require-admin.js
│   ├── models/
│   │   └── user.model.js
│   └── routes/
│       ├── auth.route.js
│       ├── index.route.js
│       └── user.route.js
├── src/
│   ├── _settings.scss
│   ├── app/
│   │   ├── admin/
│   │   │   ├── admin-routing.module.ts
│   │   │   ├── admin-user-guard.ts
│   │   │   ├── admin.component.html
│   │   │   ├── admin.component.ts
│   │   │   └── admin.module.ts
│   │   ├── app-routing.module.ts
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── auth/
│   │   │   ├── auth-routing.module.ts
│   │   │   ├── auth.component.scss
│   │   │   ├── auth.module.ts
│   │   │   ├── login/
│   │   │   │   ├── login.component.html
│   │   │   │   ├── login.component.spec.ts
│   │   │   │   └── login.component.ts
│   │   │   └── register/
│   │   │       ├── register.component.html
│   │   │       ├── register.component.spec.ts
│   │   │       └── register.component.ts
│   │   ├── header/
│   │   │   ├── header.component.html
│   │   │   ├── header.component.scss
│   │   │   ├── header.component.spec.ts
│   │   │   └── header.component.ts
│   │   ├── home/
│   │   │   ├── home.component.html
│   │   │   ├── home.component.scss
│   │   │   ├── home.component.spec.ts
│   │   │   └── home.component.ts
│   │   ├── interceptors/
│   │   │   ├── header.interceptor.ts
│   │   │   └── http-error.interceptor.ts
│   │   └── shared/
│   │       ├── guards/
│   │       │   ├── auth.guard.ts
│   │       │   └── index.ts
│   │       ├── interfaces/
│   │       │   ├── index.ts
│   │       │   └── user.interface.ts
│   │       ├── services/
│   │       │   ├── auth/
│   │       │   │   ├── auth.service.spec.ts
│   │       │   │   ├── auth.service.ts
│   │       │   │   └── token.storage.ts
│   │       │   └── index.ts
│   │       └── shared.module.ts
│   ├── assets/
│   │   └── .gitkeep
│   ├── environments/
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── index.html
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.scss
│   └── test.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
.git/
dist/
examples/
node_modules/


================================================
FILE: .editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false


================================================
FILE: .eslintrc.json
================================================
{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "parserOptions": {
        "project": ["tsconfig.json"],
        "createDefaultProgram": true
      },
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates"
      ],
      "rules": {}
    },
    {
      "files": ["*.html"],
      "extends": ["plugin:@angular-eslint/template/recommended"],
      "rules": {}
    }
  ]
}


================================================
FILE: .gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
/dist
/dist-server
/tmp
/out-tsc

# dependencies
/node_modules

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# e2e
/e2e/*.js
/e2e/*.map

# System Files
.DS_Store
Thumbs.db

# Env file
*.env


================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged


================================================
FILE: .prettierrc
================================================
{
  "singleQuote": true,
  "endOfLine": "lf",
  "trailingComma": "es5",
  "tabWidth": 2,
  "arrowParens": "avoid"
}


================================================
FILE: CNAME
================================================
www.mean.io


================================================
FILE: Dockerfile
================================================
FROM node:14.18-alpine

WORKDIR /usr/src/app
COPY . /usr/src/app

ENV HUSKY_SKIP_INSTALL=true
RUN yarn --pure-lockfile --non-interactive --no-progress
RUN yarn build:prod

EXPOSE 4040

CMD ["yarn", "serve"]


================================================
FILE: README.md
================================================
## Welcome to the mean stack

The mean stack is intended to provide a simple and fun starting point for cloud native fullstack javascript applications.  
MEAN is a set of Open Source components that together, provide an end-to-end framework for building dynamic web applications; starting from the top (code running in the browser) to the bottom (database). The stack is made up of:

- **M**ongoDB : Document database – used by your back-end application to store its data as JSON (JavaScript Object Notation) documents
- **E**xpress (sometimes referred to as Express.js): Back-end web application framework running on top of Node.js
- **A**ngular (formerly Angular.js): Front-end web app framework; runs your JavaScript code in the user's browser, allowing your application UI to be dynamic
- **N**ode.js : JavaScript runtime environment – lets you implement your application back-end in JavaScript

### Pre-requisites

- git - [Installation guide](https://www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/) .
- node.js - [Download page](https://nodejs.org/en/download/) .
- npm - comes with node or download yarn - [Download page](https://yarnpkg.com/lang/en/docs/install) .
- mongodb - [Download page](https://www.mongodb.com/download-center/community) .

### Installation

```
git clone https://github.com/linnovate/mean
cd mean
cp .env.example .env
yarn
yarn start (for development)
```

### Docker based

> ⚠️ Make sure your Docker version is 19.03.0+.

```
git clone https://github.com/linnovate/mean
cd mean
cp .env.example .env
docker-compose up -d
```

### Credits

- The MEAN name was coined by Valeri Karpov.
- Initial concept and development were done by Amos Haviv and sponsored by Linnovate.
- Inspired by the great work of Madhusudhan Srinivasa.


================================================
FILE: _config.yml
================================================
theme: jekyll-theme-minimal
logo: https://www.linnovate.net/sites/all/themes/linnovate/images/mean-picture.png


================================================
FILE: _layouts/default.html
================================================
<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

{% seo %}
    <link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
    <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="wrapper">
      <header>
        {% if site.logo %}
          <img class="logo"  src="{{site.logo | relative_url}}" alt="Logo" />
        {% endif %}
        <p>{{ site.description | default: site.github.project_tagline }}</p>

        {% if site.github.is_project_page %}
        <p class="view"><a href="{{ site.github.repository_url }}">View the Project on GitHub <small>{{ site.github.repository_nwo }}</small></a></p>
        {% endif %}

        <!-- Place this tag where you want the button to render. -->
        <a class="github-button" href="https://github.com/linnovate/mean" data-show-count="true" aria-label="Star ntkme/github-buttons on GitHub">Star</a>

        {% if site.github.is_user_page %}
        <p class="view"><a href="{{ site.github.owner_url }}">View My GitHub Profile</a></p>
        {% endif %}

        {% if site.show_downloads %}
        <ul class="downloads">
          <li><a href="{{ site.github.zip_url }}">Download <strong>ZIP File</strong></a></li>
          <li><a href="{{ site.github.tar_url }}">Download <strong>TAR Ball</strong></a></li>
          <li><a href="{{ site.github.repository_url }}">View On <strong>GitHub</strong></a></li>
        </ul>
        {% endif %}
        <img class="ninja" src="/assets/img/ninja.jpg"/>

      </header>
      <section>

      {{ content }}
      {% if site.github.is_project_page %}
      <p>This project is maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
      {% endif %}
      </section>
    </div>
    <script src="{{ "/assets/js/scale.fix.js" | relative_url }}"></script>
   <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-36499287-4"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

 // gtag('config', 'UA-XXXXXX-XX'); // change this to your own UA config
</script>

    <!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
  </body>
</html>


================================================
FILE: angular.json
================================================
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "mean": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist",
            "index": "src/index.html",
            "main": "src/main.ts",
            "tsConfig": "./tsconfig.app.json",
            "polyfills": "src/polyfills.ts",
            "assets": ["src/assets", "src/favicon.ico"],
            "styles": ["src/styles.scss"],
            "scripts": [],
            "aot": false,
            "vendorChunk": true,
            "extractLicenses": false,
            "buildOptimizer": false,
            "sourceMap": true,
            "optimization": false,
            "namedChunks": true
          },
          "configurations": {
            "production": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "mean:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "mean:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "mean:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "karmaConfig": "./karma.conf.js",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "./tsconfig.spec.json",
            "scripts": [],
            "styles": ["src/styles.scss"],
            "assets": ["src/assets", "src/favicon.ico"]
          }
        },
        "lint": {
          "builder": "@angular-eslint/builder:lint",
          "options": {
            "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
          }
        }
      }
    }
  },
  "schematics": {
    "@schematics/angular:component": {
      "prefix": "app",
      "style": "scss"
    },
    "@schematics/angular:directive": {
      "prefix": "app"
    }
  }
}


================================================
FILE: assets/css/style.scss
================================================
---
---

@import "{{ site.theme }}";
h1 {
  a {
    color:#00758f;
    text-size:40px;
  }
}
header {
 img.logo {
    margin-left:30px;
    display:block;
    height: auto; 
    width: auto; 
    max-width: 150px; 
    max-height: 200px;
    margin-bottom: 17%;
 }
 img.ninja {
  margin-top: 20px;
  height: auto; 
  width: auto; 
  max-width: 350px; 
  max-height: 200px;
  margin-bottom: 50px;
 }

} 


================================================
FILE: docker-compose.yml
================================================
version: '3.8'

services:
  app:
    build: ./
    image: mean
    ports:
      - 4040:4040
    environment:
      NODE_ENV: production
      SERVER_PORT: 4040
      JWT_SECRET: 0a6b944d-d2fb-46fc-a85e-0295c986cd9f
      MONGO_HOST: mongodb://mongo/mean
    restart: always
    depends_on:
      - mongo

  mongo:
    image: mongo:4.2
    volumes:
      - mongo_data:/data/db

volumes:
  mongo_data:


================================================
FILE: karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma'),
    ],
    client: {
      clearContext: false, // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, 'coverage'),
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true,
    },
    angularCli: {
      environment: 'dev',
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
  });
};


================================================
FILE: lint-staged.config.mjs
================================================
export default {
  '*.{js,ts,html,scss,md,json}': ['prettier --write'],
};


================================================
FILE: package.json
================================================
{
  "name": "mean",
  "version": "2.0.2",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "serve": "node server",
    "start": "concurrently -c \"yellow.bold,green.bold\" -n \"SERVER,BUILD\" \"nodemon server\" \"ng build --watch\"",
    "build:prod": "ng build --configuration production",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "postinstall": "husky install"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^15.2.9",
    "@angular/cdk": "^15.2.9",
    "@angular/common": "^15.2.9",
    "@angular/compiler": "^15.2.9",
    "@angular/core": "^15.2.9",
    "@angular/forms": "^15.2.9",
    "@angular/material": "^15.2.9",
    "@angular/platform-browser": "^15.2.9",
    "@angular/platform-browser-dynamic": "^15.2.9",
    "@angular/router": "^15.2.9",
    "bcrypt": "^5.0.1",
    "body-parser": "^1.18.2",
    "compression": "^1.7.2",
    "cookie-parser": "^1.4.3",
    "cors": "^2.8.4",
    "dotenv": "^10.0.0",
    "events": "^3.0.0",
    "express": "^4.16.3",
    "express-async-handler": "^1.1.3",
    "express-jwt": "^5.3.1",
    "express-validation": "^1.0.2",
    "formidable": "^3.1.3",
    "helmet": "^4.6.0",
    "http-errors": "^1.6.3",
    "joi": "^17.4.2",
    "jsonwebtoken": "^8.2.1",
    "method-override": "^3.0.0",
    "mongoose": "^6.0.13",
    "morgan": "^1.9.1",
    "nodemon": "^2.0.15",
    "passport": "^0.4.0",
    "passport-jwt": "^4.0.0",
    "passport-local": "^1.0.0",
    "rxjs": "^7.4.0",
    "swagger-ui-express": "^4.1.6",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^15.2.9",
    "@angular-eslint/builder": "14.0.2",
    "@angular-eslint/eslint-plugin": "14.0.2",
    "@angular-eslint/eslint-plugin-template": "14.0.2",
    "@angular-eslint/schematics": "14.0.2",
    "@angular-eslint/template-parser": "14.0.2",
    "@angular/cli": "^15.2.9",
    "@angular/compiler-cli": "^15.2.9",
    "@angular/language-service": "^15.2.9",
    "@types/jasmine": "~3.6.0",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~15.0.0",
    "@typescript-eslint/eslint-plugin": "5.3.0",
    "@typescript-eslint/parser": "5.3.0",
    "concurrently": "^3.5.1",
    "eslint": "^8.2.0",
    "husky": "^7.0.2",
    "jasmine-core": "~3.6.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~6.3.2",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "lint-staged": "^13.0.3",
    "prettier": "^2.7.1",
    "ts-node": "~6.1.0",
    "typescript": "4.9.5"
  }
}


================================================
FILE: protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './e2e/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};


================================================
FILE: server/config/config.js
================================================
const Joi = require('joi');

// require and configure dotenv, will load vars in .env in PROCESS.ENV
require('dotenv').config();

// define validation for all the env vars
const envVarsSchema = Joi.object({
  NODE_ENV: Joi.string()
    .allow('development', 'production', 'test', 'provision')
    .default('development'),
  SERVER_PORT: Joi.number().default(4040),
  MONGOOSE_DEBUG: Joi.boolean().when('NODE_ENV', {
    is: Joi.string().equal('development'),
    then: Joi.boolean().default(true),
    otherwise: Joi.boolean().default(false),
  }),
  JWT_SECRET: Joi.string()
    .required()
    .description('JWT Secret required to sign'),
  MONGO_HOST: Joi.string().required().description('Mongo DB host url'),
  MONGO_PORT: Joi.number().default(27017),
})
  .unknown()
  .required();

const { error, value: envVars } = envVarsSchema.validate(process.env);
if (error) {
  throw new Error(`Config validation error: ${error.message}`);
}

const config = {
  env: envVars.NODE_ENV,
  port: envVars.SERVER_PORT,
  mongooseDebug: envVars.MONGOOSE_DEBUG,
  jwtSecret: envVars.JWT_SECRET,
  frontend: envVars.MEAN_FRONTEND || 'angular',
  mongo: {
    host: envVars.MONGO_HOST,
    port: envVars.MONGO_PORT,
  },
};

module.exports = config;


================================================
FILE: server/config/express.js
================================================
const path = require('path');
const express = require('express');
const httpError = require('http-errors');
const logger = require('morgan');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const compress = require('compression');
const methodOverride = require('method-override');
const cors = require('cors');
const helmet = require('helmet');
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
const routes = require('../routes/index.route');
const config = require('./config');
const passport = require('./passport');

const app = express();

if (config.env === 'development') {
  app.use(logger('dev'));
}

// Choose what fronten framework to serve the dist from
var distDir = '../../dist/';
if (config.frontend == 'react') {
  distDir = '../../node_modules/material-dashboard-react/dist';
} else {
  distDir = '../../dist/';
}

//
app.use(express.static(path.join(__dirname, distDir)));
app.use(/^((?!(api)).)*/, (req, res) => {
  res.sendFile(path.join(__dirname, distDir + '/index.html'));
});

console.log(distDir);
//React server
app.use(
  express.static(
    path.join(__dirname, '../../node_modules/material-dashboard-react/dist')
  )
);
app.use(/^((?!(api)).)*/, (req, res) => {
  res.sendFile(path.join(__dirname, '../../dist/index.html'));
});

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(cookieParser());
app.use(compress());
app.use(methodOverride());

// secure apps by setting various HTTP headers
app.use(helmet());

// enable CORS - Cross Origin Resource Sharing
app.use(cors());

app.use(passport.initialize());

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

// API router
app.use('/api/', routes);

// catch 404 and forward to error handler
app.use((req, res, next) => {
  const err = new httpError(404);
  return next(err);
});

// error handler, send stacktrace only during development
app.use((err, req, res, next) => {
  // customize Joi validation errors
  if (err.isJoi) {
    err.message = err.details.map(e => e.message).join('; ');
    err.status = 400;
  }

  res.status(err.status || 500).json({
    message: err.message,
  });
  next(err);
});

module.exports = app;


================================================
FILE: server/config/mongoose.js
================================================
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');

const config = require('./config');

// connect to mongo db
const mongoUri = config.mongo.host;
mongoose.connect(mongoUri, { keepAlive: 1 });
mongoose.connection.on('error', () => {
  throw new Error(`unable to connect to database: ${mongoUri}`);
});

// print mongoose logs in dev env
if (config.MONGOOSE_DEBUG) {
  mongoose.set('debug', (collectionName, method, query, doc) => {
    debug(`${collectionName}.${method}`, util.inspect(query, false, 20), doc);
  });
}


================================================
FILE: server/config/passport.js
================================================
const passport = require('passport');
const LocalStrategy = require('passport-local');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const bcrypt = require('bcrypt');

const User = require('../models/user.model');
const config = require('./config');

const localLogin = new LocalStrategy(
  {
    usernameField: 'email',
  },
  async (email, password, done) => {
    let user = await User.findOne({ email });
    if (!user || !bcrypt.compareSync(password, user.hashedPassword)) {
      return done(null, false, {
        error: 'Your login details could not be verified. Please try again.',
      });
    }
    user = user.toObject();
    delete user.hashedPassword;
    done(null, user);
  }
);

const jwtLogin = new JwtStrategy(
  {
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
    secretOrKey: config.jwtSecret,
  },
  async (payload, done) => {
    let user = await User.findById(payload._id);
    if (!user) {
      return done(null, false);
    }
    user = user.toObject();
    delete user.hashedPassword;
    done(null, user);
  }
);

passport.use(jwtLogin);
passport.use(localLogin);

module.exports = passport;


================================================
FILE: server/config/swagger.json
================================================
{
    "swagger": "2.0",
    "info": {
      "version": "1.0.0",
      "title": "Mean Application API",
      "description": "Mean Application API",
      "license": {
        "name": "MIT",
        "url": "https://opensource.org/licenses/MIT"
      }
    },
    "host": "localhost:4040",
    "basePath": "/api/",
    "tags": [
      {
        "name": "Users",
        "description": "API for users in the system"
      },
      {
        "name": "Auth",
        "description": "API for auth in the system"
      }
    ],
    "schemes": [
      "http"
    ],
    "consumes": [
      "application/json"
    ],
    "produces": [
      "application/json"
    ],
    "securityDefinitions": {
      "AuthHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization"
      }
    },
    "paths": {
      "/auth/login": {
        "post": {
          "tags": ["Auth"],
          "description": "Login to the system",
        "parameters": [{
          "name": "auth",
          "in": "body",
          "description": "User auth details",
          "schema": {
            "type": "object",
            "required": ["email", "password"],
            "properties": {
              "email": {
                "type": "string"
              },
              "password": {
                "type": "string"
              }
            }
          }
        }],
        "produces": [
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "User is loggedin",
            "schema": {
              "$ref": "#/definitions/User"
            }
          }
        }
      }
      }
    },
    "definitions": {
      "User": {
        "required": [
          "email",
          "fullname"
        ],
        "properties": {
          "_id": {
            "type": "string",
            "uniqueItems": true
          },
          "email": {
            "type": "string",
            "uniqueItems": true
          },
          "fullname": {
            "type": "string"
          },
          "createdAt": {
            "type": "string"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Users": {
        "type": "array",
        "$ref": "#/definitions/User"
      },
      "Auth": {
        "type": "object",
        "properties": [{
          "token": {
            "type": "string"
          },
          "user": {
            "$ref": "#/definitions/User"
          }
        }]
      }
  }
}

================================================
FILE: server/controllers/auth.controller.js
================================================
const jwt = require('jsonwebtoken');
const config = require('../config/config');

module.exports = {
  generateToken,
};

function generateToken(user) {
  const payload = JSON.stringify(user);
  return jwt.sign(payload, config.jwtSecret);
}


================================================
FILE: server/controllers/user.controller.js
================================================
const bcrypt = require('bcrypt');
const Joi = require('joi');
const User = require('../models/user.model');

const userSchema = Joi.object({
  fullname: Joi.string().required(),
  email: Joi.string().email(),
  mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/),
  password: Joi.string().required(),
  repeatPassword: Joi.string().required().valid(Joi.ref('password')),
});

module.exports = {
  insert,
};

async function insert(user) {
  user = await userSchema.validateAsync(user, { abortEarly: false });
  user.hashedPassword = bcrypt.hashSync(user.password, 10);
  delete user.password;
  return await new User(user).save();
}


================================================
FILE: server/index.js
================================================
// config should be imported before importing any other file
const config = require('./config/config');
const app = require('./config/express');
require('./config/mongoose');

// module.parent check is required to support mocha watch
// src: https://github.com/mochajs/mocha/issues/1912
if (!module.parent) {
  app.listen(config.port, () => {
    console.info(`server started on port ${config.port} (${config.env})`);
  });
}

module.exports = app;


================================================
FILE: server/middleware/require-admin.js
================================================
const httpError = require('http-errors');

const requireAdmin = function (req, res, next) {
  if (req.user && req.user.roles.indexOf('admin') > -1) return next();
  const err = new httpError(401);
  return next(err);
};

module.exports = requireAdmin;


================================================
FILE: server/models/user.model.js
================================================
const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema(
  {
    fullname: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
      unique: true,
      // Regexp to validate emails with more strict rules as added in tests/users.js which also conforms mostly with RFC2822 guide lines
      match: [
        /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
        'Please enter a valid email',
      ],
    },
    hashedPassword: {
      type: String,
      required: true,
    },
    createdAt: {
      type: Date,
      default: Date.now,
    },
    roles: [
      {
        type: String,
      },
    ],
  },
  {
    versionKey: false,
  }
);

module.exports = mongoose.model('User', UserSchema);


================================================
FILE: server/routes/auth.route.js
================================================
const express = require('express');
const asyncHandler = require('express-async-handler');
const passport = require('passport');
const userCtrl = require('../controllers/user.controller');
const authCtrl = require('../controllers/auth.controller');
const config = require('../config/config');

const router = express.Router();
module.exports = router;

router.post('/register', asyncHandler(register), login);
router.post(
  '/login',
  passport.authenticate('local', { session: false }),
  login
);
router.get('/me', passport.authenticate('jwt', { session: false }), login);

async function register(req, res, next) {
  let user = await userCtrl.insert(req.body);
  user = user.toObject();
  delete user.hashedPassword;
  req.user = user;
  next();
}

function login(req, res) {
  let user = req.user;
  let token = authCtrl.generateToken(user);
  res.json({ user, token });
}


================================================
FILE: server/routes/index.route.js
================================================
const express = require('express');
const userRoutes = require('./user.route');
const authRoutes = require('./auth.route');

const router = express.Router(); // eslint-disable-line new-cap

/** GET /health-check - Check service health */
router.get('/health-check', (req, res) => res.send('OK'));

router.use('/auth', authRoutes);
router.use('/user', userRoutes);

module.exports = router;


================================================
FILE: server/routes/user.route.js
================================================
const express = require('express');
const passport = require('passport');
const asyncHandler = require('express-async-handler');
const userCtrl = require('../controllers/user.controller');

const router = express.Router();
module.exports = router;

router.use(passport.authenticate('jwt', { session: false }));

router.route('/').post(asyncHandler(insert));

async function insert(req, res) {
  let user = await userCtrl.insert(req.body);
  res.json(user);
}


================================================
FILE: src/_settings.scss
================================================
/*-----------------------------------------------
                   Variables
-----------------------------------------------*/
$linesColor: #dbdbdb;
$categoryTitleColor: #686868;
$categoryEntityColor: #3f3f3f;


================================================
FILE: src/app/admin/admin-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AdminComponent } from './admin.component';
import { OnlyAdminUsersGuard } from './admin-user-guard';

const routes: Routes = [
  {
    path: 'admin',
    canActivate: [OnlyAdminUsersGuard],
    children: [
      {
        path: '',
        component: AdminComponent,
      },
    ],
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class AdminRoutingModule {}


================================================
FILE: src/app/admin/admin-user-guard.ts
================================================
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { AuthService } from '@app/shared/services';

@Injectable()
export class OnlyAdminUsersGuard implements CanActivate {
  constructor(private authService: AuthService) {}

  canActivate(): Observable<boolean> {
    return this.authService.getUser().pipe(map(user => !!user?.isAdmin));
  }
}


================================================
FILE: src/app/admin/admin.component.html
================================================
<h4>HELLO FROM ADMIN PAGE</h4>


================================================
FILE: src/app/admin/admin.component.ts
================================================
import { Component } from '@angular/core';

@Component({
  selector: 'app-admin',
  templateUrl: './admin.component.html',
})
export class AdminComponent {}


================================================
FILE: src/app/admin/admin.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { AdminRoutingModule } from './admin-routing.module';
import { AdminComponent } from './admin.component';
import { OnlyAdminUsersGuard } from './admin-user-guard';

@NgModule({
  declarations: [AdminComponent],
  imports: [CommonModule, AdminRoutingModule],
  providers: [OnlyAdminUsersGuard],
})
export class AdminModule {}


================================================
FILE: src/app/app-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AuthGuard } from './shared/guards';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    canActivate: [AuthGuard],
  },
  {
    path: 'auth',
    loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule),
  },
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes, {})],
  exports: [RouterModule],
})
export class AppRoutingModule {}


================================================
FILE: src/app/app.component.html
================================================
<app-header [user]="user$ | async"></app-header>
<div class="wrapper-app">
  <router-outlet></router-outlet>
</div>
<footer></footer>


================================================
FILE: src/app/app.component.scss
================================================
.wrapper-app {
}


================================================
FILE: src/app/app.component.ts
================================================
import { Component } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';

import { merge, Observable } from 'rxjs';

import { User } from './shared/interfaces';
import { AuthService } from './shared/services';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  user$: Observable<User | null> = merge(
    // Init on startup
    this.authService.me(),
    // Update after login/register/logout
    this.authService.getUser()
  );

  constructor(
    private domSanitizer: DomSanitizer,
    private matIconRegistry: MatIconRegistry,
    private authService: AuthService
  ) {
    this.registerSvgIcons();
  }

  registerSvgIcons() {
    [
      'close',
      'add',
      'add-blue',
      'airplane-front-view',
      'air-station',
      'balloon',
      'boat',
      'cargo-ship',
      'car',
      'catamaran',
      'clone',
      'convertible',
      'delete',
      'drone',
      'fighter-plane',
      'fire-truck',
      'horseback-riding',
      'motorcycle',
      'railcar',
      'railroad-train',
      'rocket-boot',
      'sailing-boat',
      'segway',
      'shuttle',
      'space-shuttle',
      'steam-engine',
      'suv',
      'tour-bus',
      'tow-truck',
      'transportation',
      'trolleybus',
      'water-transportation',
    ].forEach(icon => {
      this.matIconRegistry.addSvgIcon(
        icon,
        this.domSanitizer.bypassSecurityTrustResourceUrl(
          `assets/icons/${icon}.svg`
        )
      );
    });
  }
}


================================================
FILE: src/app/app.module.ts
================================================
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { SharedModule } from './shared/shared.module';

import { AppComponent } from './app.component';
import { AuthHeaderInterceptor } from './interceptors/header.interceptor';
import { CatchErrorInterceptor } from './interceptors/http-error.interceptor';

import { AppRoutingModule } from './app-routing.module';
import { HeaderComponent } from './header/header.component';
import { HomeComponent } from './home/home.component';
import { AuthService } from './shared/services';

export function appInitializerFactory(authService: AuthService) {
  return () => authService.checkTheUserOnTheFirstLoad();
}

@NgModule({
  imports: [
    BrowserAnimationsModule,
    HttpClientModule,
    SharedModule,
    AppRoutingModule,
  ],
  declarations: [AppComponent, HeaderComponent, HomeComponent],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthHeaderInterceptor,
      multi: true,
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: CatchErrorInterceptor,
      multi: true,
    },
    {
      provide: APP_INITIALIZER,
      useFactory: appInitializerFactory,
      multi: true,
      deps: [AuthService],
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}


================================================
FILE: src/app/auth/auth-routing.module.ts
================================================
import { Routes, RouterModule } from '@angular/router';

import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';

const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: '',
        redirectTo: '/auth/login',
        pathMatch: 'full',
      },
      {
        path: 'login',
        component: LoginComponent,
      },
      {
        path: 'register',
        component: RegisterComponent,
      },
    ],
  },
];

export const AuthRoutingModule = RouterModule.forChild(routes);


================================================
FILE: src/app/auth/auth.component.scss
================================================
.example-icon {
  padding: 0 14px;
}

.example-spacer {
  flex: 1 1 auto;
}

.example-card {
  width: 400px;
  margin: 10% auto;
}

.mat-card-title {
  font-size: 16px;
}


================================================
FILE: src/app/auth/auth.module.ts
================================================
import { NgModule } from '@angular/core';

import { SharedModule } from '../shared/shared.module';

import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { AuthRoutingModule } from './auth-routing.module';

@NgModule({
  imports: [SharedModule, AuthRoutingModule],
  declarations: [LoginComponent, RegisterComponent],
})
export class AuthModule {}


================================================
FILE: src/app/auth/login/login.component.html
================================================
<mat-card class="example-card">
  <mat-card-header>
    <mat-card-title>Login</mat-card-title>
  </mat-card-header>
  <mat-card-content>
    <form class="example-form">
      <table cellspacing="0">
        <tr>
          <td>
            <mat-form-field>
              <input
                matInput
                placeholder="Email"
                [(ngModel)]="email"
                name="email"
                required
              />
            </mat-form-field>
          </td>
        </tr>
        <tr>
          <td>
            <mat-form-field>
              <input
                matInput
                placeholder="Password"
                [(ngModel)]="password"
                type="password"
                name="password"
                required
              />
            </mat-form-field>
          </td>
        </tr>
      </table>
    </form>
  </mat-card-content>
  <mat-card-actions>
    <button mat-raised-button (click)="login()" color="primary">Login</button>
    <span
      >Don't have an account ?
      <a [routerLink]="['/auth/register']">register</a> here</span
    >
  </mat-card-actions>
</mat-card>


================================================
FILE: src/app/auth/login/login.component.spec.ts
================================================
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { LoginComponent } from './login.component';

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [LoginComponent],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/auth/login/login.component.ts
================================================
import { Component } from '@angular/core';
import { Router } from '@angular/router';

import { AuthService } from '@app/shared/services';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['../auth.component.scss'],
})
export class LoginComponent {
  email: string | null = null;
  password: string | null = null;

  constructor(private router: Router, private authService: AuthService) {}

  login(): void {
    this.authService.login(this.email!, this.password!).subscribe(() => {
      this.router.navigateByUrl('/');
    });
  }
}


================================================
FILE: src/app/auth/register/register.component.html
================================================
<mat-card class="example-card">
  <mat-card-header>
    <mat-card-title>Register</mat-card-title>
  </mat-card-header>
  <mat-card-content>
    <form class="example-form">
      <table cellspacing="0" [formGroup]="userForm">
        <tr>
          <td>
            <mat-form-field>
              <input
                matInput
                placeholder="Fullname"
                formControlName="fullname"
                name="fullname"
                required
              />
            </mat-form-field>
          </td>
        </tr>
        <tr>
          <td>
            <mat-form-field>
              <input
                matInput
                placeholder="Email"
                formControlName="email"
                name="email"
                required
              />
              <mat-error *ngIf="email.invalid && email.hasError('email')"
                >Invalid email address</mat-error
              >
            </mat-form-field>
          </td>
        </tr>
        <tr>
          <td>
            <mat-form-field>
              <input
                matInput
                placeholder="Password"
                formControlName="password"
                type="password"
                name="password"
                required
              />
            </mat-form-field>
          </td>
        </tr>
        <tr>
          <td>
            <mat-form-field>
              <input
                matInput
                placeholder="Repeat Password"
                formControlName="repeatPassword"
                type="password"
                name="repeatPassword"
                required
              />
              <mat-error
                *ngIf="
                  repeatPassword.invalid &&
                  repeatPassword.hasError('passwordMatch')
                "
                >Password mismatch</mat-error
              >
            </mat-form-field>
          </td>
        </tr>
      </table>
    </form>
  </mat-card-content>
  <mat-card-actions>
    <button mat-raised-button (click)="register()" color="primary">
      Register
    </button>
    <span
      >Already have an account ?
      <a [routerLink]="['/auth/login']">login</a> here</span
    >
  </mat-card-actions>
</mat-card>


================================================
FILE: src/app/auth/register/register.component.spec.ts
================================================
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { RegisterComponent } from './register.component';

describe('RegisterComponent', () => {
  let component: RegisterComponent;
  let fixture: ComponentFixture<RegisterComponent>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [RegisterComponent],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(RegisterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/auth/register/register.component.ts
================================================
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import {
  UntypedFormGroup,
  UntypedFormControl,
  Validators,
  ValidationErrors,
  AbstractControl,
} from '@angular/forms';

import { AuthService } from '@app/shared/services';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['../auth.component.scss'],
})
export class RegisterComponent {
  constructor(private router: Router, private authService: AuthService) {}

  passwordsMatchValidator(
    control: UntypedFormControl
  ): ValidationErrors | null {
    const password = control.root.get('password');
    return password && control.value !== password.value
      ? {
          passwordMatch: true,
        }
      : null;
  }

  userForm = new UntypedFormGroup({
    fullname: new UntypedFormControl('', [Validators.required]),
    email: new UntypedFormControl('', [Validators.required, Validators.email]),
    password: new UntypedFormControl('', [Validators.required]),
    repeatPassword: new UntypedFormControl('', [
      Validators.required,
      this.passwordsMatchValidator,
    ]),
  });

  get fullname(): AbstractControl {
    return this.userForm.get('fullname')!;
  }

  get email(): AbstractControl {
    return this.userForm.get('email')!;
  }

  get password(): AbstractControl {
    return this.userForm.get('password')!;
  }

  get repeatPassword(): AbstractControl {
    return this.userForm.get('repeatPassword')!;
  }

  register(): void {
    if (this.userForm.invalid) {
      return;
    }

    const { fullname, email, password, repeatPassword } =
      this.userForm.getRawValue();

    this.authService
      .register(fullname, email, password, repeatPassword)
      .subscribe(() => {
        this.router.navigate(['']);
      });
  }
}


================================================
FILE: src/app/header/header.component.html
================================================
<header>
  <mat-toolbar color="primary">
    <a routerLink="/" class="logo"></a>
    <span class="example-spacer"></span>
    <a class="links side" routerLink="/auth/login" *ngIf="!user">Login</a>
    <div>
      <a class="links side" *ngIf="user" [matMenuTriggerFor]="menu">
        <mat-icon>account_circle</mat-icon>{{ user.fullname }}
      </a>
      <mat-menu #menu="matMenu">
        <button mat-menu-item *ngIf="user?.isAdmin" routerLink="/admin">
          admin
        </button>
        <button mat-menu-item (click)="logout()">logout</button>
      </mat-menu>
    </div>
  </mat-toolbar>
</header>


================================================
FILE: src/app/header/header.component.scss
================================================
header {
  width: 100%;
  .logo {
    background-image: url('../../assets/logo.png');
    width: 50px;
    height: 50px;
    background-size: contain;
    background-repeat: no-repeat;
  }
  .example-spacer {
    flex: 1 1 auto;
  }
  .links {
    color: white;
    font-family: 'Helvetica Neue', sans-serif;
    font-size: 15px;
    font-weight: initial;
    letter-spacing: -1px;
    line-height: 1;
    text-align: center;
    padding: 15px;
    &.side {
      padding: 0 14px;
    }
  }
  .mat-toolbar {
    background: black;
  }
  .mat-icon {
    vertical-align: middle;
    margin: 0 5px;
  }
  a {
    cursor: pointer;
  }
}


================================================
FILE: src/app/header/header.component.spec.ts
================================================
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [HeaderComponent],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/header/header.component.ts
================================================
import { Component, Input } from '@angular/core';
import { Router } from '@angular/router';

import { User } from '@app/shared/interfaces';

import { AuthService } from '@app/shared/services';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
  @Input() user: User | null = null;

  constructor(private router: Router, private authService: AuthService) {}

  logout(): void {
    this.authService.signOut();
    this.router.navigateByUrl('/auth/login');
  }
}


================================================
FILE: src/app/home/home.component.html
================================================
<p>home works!</p>


================================================
FILE: src/app/home/home.component.scss
================================================


================================================
FILE: src/app/home/home.component.spec.ts
================================================
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { HomeComponent } from './home.component';

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [HomeComponent],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(HomeComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss'],
})
export class HomeComponent {}


================================================
FILE: src/app/interceptors/header.interceptor.ts
================================================
import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
} from '@angular/common/http';

import { Observable } from 'rxjs';

import { AuthService } from '@app/shared/services';

@Injectable()
export class AuthHeaderInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    req = req.clone({
      setHeaders: this.authService.getAuthorizationHeaders(),
    });

    return next.handle(req);
  }
}


================================================
FILE: src/app/interceptors/http-error.interceptor.ts
================================================
import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpErrorResponse,
} from '@angular/common/http';
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';

import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class CatchErrorInterceptor implements HttpInterceptor {
  constructor(private snackBar: MatSnackBar) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(catchError(this.showSnackBar));
  }

  private showSnackBar = (response: HttpErrorResponse): Observable<never> => {
    const text: string | undefined =
      response.error?.message ?? response.error.statusText;

    if (text) {
      this.snackBar.open(text, 'Close', {
        duration: 2000,
      });
    }

    return throwError(() => response);
  };
}


================================================
FILE: src/app/shared/guards/auth.guard.ts
================================================
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { AuthService } from '../services';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private router: Router, private authService: AuthService) {}

  canActivate(): Observable<boolean> {
    return this.authService.getUser().pipe(
      map(user => {
        if (user !== null) {
          return true;
        }

        this.router.navigateByUrl('/auth/login');
        return false;
      })
    );
  }
}


================================================
FILE: src/app/shared/guards/index.ts
================================================
export * from './auth.guard';


================================================
FILE: src/app/shared/interfaces/index.ts
================================================
export * from './user.interface';


================================================
FILE: src/app/shared/interfaces/user.interface.ts
================================================
export interface User {
  _id: string;
  fullname: string;
  createdAt: string;
  roles: string[];
  isAdmin: boolean;
}


================================================
FILE: src/app/shared/services/auth/auth.service.spec.ts
================================================
import { TestBed, inject } from '@angular/core/testing';

import { AuthService } from './auth.service';

describe('AuthService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [AuthService],
    });
  });

  it('should be created', inject([AuthService], (service: AuthService) => {
    expect(service).toBeTruthy();
  }));
});


================================================
FILE: src/app/shared/services/auth/auth.service.ts
================================================
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable, BehaviorSubject, firstValueFrom, of } from 'rxjs';
import { tap, pluck, catchError } from 'rxjs/operators';

import { User } from '@app/shared/interfaces';

import { TokenStorage } from './token.storage';

interface AuthResponse {
  token: string;
  user: User;
}

@Injectable({ providedIn: 'root' })
export class AuthService {
  private user$ = new BehaviorSubject<User | null>(null);

  constructor(private http: HttpClient, private tokenStorage: TokenStorage) {}

  login(email: string, password: string): Observable<User> {
    return this.http
      .post<AuthResponse>('/api/auth/login', { email, password })
      .pipe(
        tap(({ token, user }) => {
          this.setUser(user);
          this.tokenStorage.saveToken(token);
        }),
        pluck('user')
      );
  }

  register(
    fullname: string,
    email: string,
    password: string,
    repeatPassword: string
  ): Observable<User> {
    return this.http
      .post<AuthResponse>('/api/auth/register', {
        fullname,
        email,
        password,
        repeatPassword,
      })
      .pipe(
        tap(({ token, user }) => {
          this.setUser(user);
          this.tokenStorage.saveToken(token);
        }),
        pluck('user')
      );
  }

  setUser(user: User | null): void {
    if (user) {
      user.isAdmin = user.roles.includes('admin');
    }

    this.user$.next(user);
  }

  getUser(): Observable<User | null> {
    return this.user$.asObservable();
  }

  me(): Observable<User | null> {
    return this.http.get<AuthResponse>('/api/auth/me').pipe(
      tap(({ user }) => this.setUser(user)),
      pluck('user'),
      catchError(() => of(null))
    );
  }

  signOut(): void {
    this.tokenStorage.signOut();
    this.setUser(null);
  }

  getAuthorizationHeaders() {
    const token: string | null = this.tokenStorage.getToken() || '';
    return { Authorization: `Bearer ${token}` };
  }

  /**
   * Let's try to get user's information if he was logged in previously,
   * thus we can ensure that the user is able to access the `/` (home) page.
   */
  checkTheUserOnTheFirstLoad(): Promise<User | null> {
    return firstValueFrom(this.me());
  }
}


================================================
FILE: src/app/shared/services/auth/token.storage.ts
================================================
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class TokenStorage {
  private tokenKey = 'authToken';

  signOut(): void {
    localStorage.removeItem(this.tokenKey);
    localStorage.clear();
  }

  saveToken(token?: string): void {
    if (!token) return;
    localStorage.setItem(this.tokenKey, token);
  }

  getToken(): string | null {
    return localStorage.getItem(this.tokenKey);
  }
}


================================================
FILE: src/app/shared/services/index.ts
================================================
export * from './auth/auth.service';


================================================
FILE: src/app/shared/shared.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatLegacyTabsModule as MatTabsModule } from '@angular/material/legacy-tabs';
import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { MatLegacyListModule as MatListModule } from '@angular/material/legacy-list';
import { MatIconModule } from '@angular/material/icon';
import { MatTreeModule } from '@angular/material/tree';
import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatDividerModule } from '@angular/material/divider';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/legacy-snack-bar';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar';

@NgModule({
  exports: [
    FormsModule,
    ReactiveFormsModule,
    CommonModule,
    MatMenuModule,
    MatTabsModule,
    MatCardModule,
    MatListModule,
    MatIconModule,
    MatTreeModule,
    MatInputModule,
    MatSelectModule,
    MatDialogModule,
    MatButtonModule,
    MatDividerModule,
    MatToolbarModule,
    MatSidenavModule,
    MatSnackBarModule,
    MatExpansionModule,
    MatFormFieldModule,
    MatProgressBarModule,
  ],
})
export class SharedModule {}


================================================
FILE: src/assets/.gitkeep
================================================


================================================
FILE: src/environments/environment.prod.ts
================================================
export const environment = {
  production: true,
};


================================================
FILE: src/environments/environment.ts
================================================
export const environment = {
  production: false,
};


================================================
FILE: src/index.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Mean</title>
    <base href="/" />

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />

    <link
      rel="prefetch"
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      as="style"
      crossorigin
    />
    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />

    <link
      href="https://fonts.googleapis.com/css?family=Exo:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i"
      rel="stylesheet"
    />
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>


================================================
FILE: src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.log(err));


================================================
FILE: src/polyfills.ts
================================================
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/guide/browser-support
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/**
 * By default, zone.js will patch all possible macroTask and DomEvents
 * user can disable parts of macroTask/DomEvents patch by setting following flags
 * because those flags need to be set before `zone.js` being loaded, and webpack
 * will put import in the top of bundle, so user need to create a separate file
 * in this directory (for example: zone-flags.ts), and put the following flags
 * into that file, and then add the following code before importing zone.js.
 * import './zone-flags.ts';
 *
 * The flags allowed in zone-flags.ts are listed here.
 *
 * The following flags will work for all browsers.
 *
 * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
 *
 *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 *  with the following flag, it will bypass `zone.js` patch for IE/Edge
 *
 *  (window as any).__Zone_enable_cross_context_check = true;
 *
 */

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import 'zone.js'; // Included with Angular CLI.


================================================
FILE: src/styles.scss
================================================
/* You can add global styles to this file, and also import other style files */
@use '@angular/material' as mat;
@import '@angular/material/prebuilt-themes/deeppurple-amber.css';

$custom-typography: mat.define-legacy-typography-config(
  $font-family: 'Exo',
);

// TODO(v15): As of v15 mat.legacy-core no longer includes default typography styles.
//  The following line adds:
//    1. Default typography styles for all components
//    2. Styles for typography hierarchy classes (e.g. .mat-headline-1)
//  If you specify typography styles for the components you use elsewhere, you should delete this line.
//  If you don't need the default component typographies but still want the hierarchy styles,
//  you can delete this line and instead use:
//    `@include mat.legacy-typography-hierarchy($custom-typography);`
@include mat.all-legacy-component-typographies($custom-typography);
@include mat.legacy-core();

body {
  background-color: #f4f3f3;
  margin: 0;
  app-root * {
    font-family: 'Exo', sans-serif;
  }
}


================================================
FILE: src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting(),
  {
    teardown: { destroyAfterEach: true },
  }
);


================================================
FILE: tsconfig.app.json
================================================
{
  "extends": "./tsconfig.json",
  "files": [
    "src/polyfills.ts",
    "src/main.ts"
  ],
  "include": [
    "src/**/*.d.ts"
  ],
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@app/*": ["src/app/*"]
    },
    "target": "ES2022",
    "typeRoots": ["node_modules/@types"],
    "lib": ["ESNext", "DOM"],
    "module": "es2020",
    "moduleResolution": "node",
    "strict": true,
    "sourceMap": true,
    "declaration": false,
    "importHelpers": true,
    "experimentalDecorators": true,
    "suppressImplicitAnyIndexErrors": true,
    "useDefineForClassFields": false
  }
}


================================================
FILE: tsconfig.spec.json
================================================
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "baseUrl": "./",
    "module": "commonjs",
    "types": [
      "jasmine",
      "node"
    ]
  },
  "files": [
    "src/test.ts",
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.spec.ts",
    "src/**/*.d.ts"
  ]
}


================================================
FILE: tslint.json
================================================
{
  "rulesDirectory": [
    "node_modules/codelyzer"
  ],
  "rules": {
    "arrow-return-shorthand": true,
    "callable-types": true,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "deprecation": {
      "severity": "warn"
    },
    "eofline": true,
    "forin": true,
    "import-blacklist": [
      true,
      "rxjs/Rx"
    ],
    "import-spacing": true,
    "indent": [
      true,
      "spaces"
    ],
    "interface-over-type-literal": true,
    "label-position": true,
    "max-line-length": [
      true,
      140
    ],
    "member-access": false,
    "member-ordering": [
      true,
      {
        "order": [
          "static-field",
          "instance-field",
          "static-method",
          "instance-method"
        ]
      }
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-super": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-eval": true,
    "no-inferrable-types": [
      true,
      "ignore-params"
    ],
    "no-misused-new": true,
    "no-non-null-assertion": true,
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": true,
    "no-unnecessary-initializer": true,
    "no-unused-expression": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "prefer-const": true,
    "quotemark": [
      true,
      "single"
    ],
    "radix": true,
    "semicolon": [
      true,
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "unified-signatures": true,
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],
    "directive-selector": [
      true,
      "attribute",
      "app",
      "camelCase"
    ],
    "component-selector": [
      true,
      "element",
      "app",
      "kebab-case"
    ],
    "no-output-on-prefix": true,
    "no-inputs-metadata-property": true,
    "no-outputs-metadata-property": true,
    "no-host-metadata-property": true,
    "no-input-rename": true,
    "no-output-rename": true,
    "use-lifecycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true
  }
}
Download .txt
gitextract_bnkb_igt/

├── .dockerignore
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierrc
├── CNAME
├── Dockerfile
├── README.md
├── _config.yml
├── _layouts/
│   └── default.html
├── angular.json
├── assets/
│   └── css/
│       └── style.scss
├── docker-compose.yml
├── karma.conf.js
├── lint-staged.config.mjs
├── package.json
├── protractor.conf.js
├── server/
│   ├── config/
│   │   ├── config.js
│   │   ├── express.js
│   │   ├── mongoose.js
│   │   ├── passport.js
│   │   └── swagger.json
│   ├── controllers/
│   │   ├── auth.controller.js
│   │   └── user.controller.js
│   ├── index.js
│   ├── middleware/
│   │   └── require-admin.js
│   ├── models/
│   │   └── user.model.js
│   └── routes/
│       ├── auth.route.js
│       ├── index.route.js
│       └── user.route.js
├── src/
│   ├── _settings.scss
│   ├── app/
│   │   ├── admin/
│   │   │   ├── admin-routing.module.ts
│   │   │   ├── admin-user-guard.ts
│   │   │   ├── admin.component.html
│   │   │   ├── admin.component.ts
│   │   │   └── admin.module.ts
│   │   ├── app-routing.module.ts
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── auth/
│   │   │   ├── auth-routing.module.ts
│   │   │   ├── auth.component.scss
│   │   │   ├── auth.module.ts
│   │   │   ├── login/
│   │   │   │   ├── login.component.html
│   │   │   │   ├── login.component.spec.ts
│   │   │   │   └── login.component.ts
│   │   │   └── register/
│   │   │       ├── register.component.html
│   │   │       ├── register.component.spec.ts
│   │   │       └── register.component.ts
│   │   ├── header/
│   │   │   ├── header.component.html
│   │   │   ├── header.component.scss
│   │   │   ├── header.component.spec.ts
│   │   │   └── header.component.ts
│   │   ├── home/
│   │   │   ├── home.component.html
│   │   │   ├── home.component.scss
│   │   │   ├── home.component.spec.ts
│   │   │   └── home.component.ts
│   │   ├── interceptors/
│   │   │   ├── header.interceptor.ts
│   │   │   └── http-error.interceptor.ts
│   │   └── shared/
│   │       ├── guards/
│   │       │   ├── auth.guard.ts
│   │       │   └── index.ts
│   │       ├── interfaces/
│   │       │   ├── index.ts
│   │       │   └── user.interface.ts
│   │       ├── services/
│   │       │   ├── auth/
│   │       │   │   ├── auth.service.spec.ts
│   │       │   │   ├── auth.service.ts
│   │       │   │   └── token.storage.ts
│   │       │   └── index.ts
│   │       └── shared.module.ts
│   ├── assets/
│   │   └── .gitkeep
│   ├── environments/
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── index.html
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.scss
│   └── test.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json
Download .txt
SYMBOL INDEX (60 symbols across 24 files)

FILE: protractor.conf.js
  method onPrepare (line 22) | onPrepare() {

FILE: server/controllers/auth.controller.js
  function generateToken (line 8) | function generateToken(user) {

FILE: server/controllers/user.controller.js
  function insert (line 17) | async function insert(user) {

FILE: server/routes/auth.route.js
  function register (line 19) | async function register(req, res, next) {
  function login (line 27) | function login(req, res) {

FILE: server/routes/user.route.js
  function insert (line 13) | async function insert(req, res) {

FILE: src/app/admin/admin-routing.module.ts
  class AdminRoutingModule (line 24) | class AdminRoutingModule {}

FILE: src/app/admin/admin-user-guard.ts
  class OnlyAdminUsersGuard (line 10) | class OnlyAdminUsersGuard implements CanActivate {
    method constructor (line 11) | constructor(private authService: AuthService) {}
    method canActivate (line 13) | canActivate(): Observable<boolean> {

FILE: src/app/admin/admin.component.ts
  class AdminComponent (line 7) | class AdminComponent {}

FILE: src/app/admin/admin.module.ts
  class AdminModule (line 13) | class AdminModule {}

FILE: src/app/app-routing.module.ts
  class AppRoutingModule (line 27) | class AppRoutingModule {}

FILE: src/app/app.component.ts
  class AppComponent (line 15) | class AppComponent {
    method constructor (line 23) | constructor(
    method registerSvgIcons (line 31) | registerSvgIcons() {

FILE: src/app/app.module.ts
  function appInitializerFactory (line 16) | function appInitializerFactory(authService: AuthService) {
  class AppModule (line 48) | class AppModule {}

FILE: src/app/auth/auth.module.ts
  class AuthModule (line 13) | class AuthModule {}

FILE: src/app/auth/login/login.component.ts
  class LoginComponent (line 11) | class LoginComponent {
    method constructor (line 15) | constructor(private router: Router, private authService: AuthService) {}
    method login (line 17) | login(): void {

FILE: src/app/auth/register/register.component.ts
  class RegisterComponent (line 18) | class RegisterComponent {
    method constructor (line 19) | constructor(private router: Router, private authService: AuthService) {}
    method passwordsMatchValidator (line 21) | passwordsMatchValidator(
    method fullname (line 42) | get fullname(): AbstractControl {
    method email (line 46) | get email(): AbstractControl {
    method password (line 50) | get password(): AbstractControl {
    method repeatPassword (line 54) | get repeatPassword(): AbstractControl {
    method register (line 58) | register(): void {

FILE: src/app/header/header.component.ts
  class HeaderComponent (line 13) | class HeaderComponent {
    method constructor (line 16) | constructor(private router: Router, private authService: AuthService) {}
    method logout (line 18) | logout(): void {

FILE: src/app/home/home.component.ts
  class HomeComponent (line 8) | class HomeComponent {}

FILE: src/app/interceptors/header.interceptor.ts
  class AuthHeaderInterceptor (line 14) | class AuthHeaderInterceptor implements HttpInterceptor {
    method constructor (line 15) | constructor(private authService: AuthService) {}
    method intercept (line 17) | intercept(

FILE: src/app/interceptors/http-error.interceptor.ts
  class CatchErrorInterceptor (line 15) | class CatchErrorInterceptor implements HttpInterceptor {
    method constructor (line 16) | constructor(private snackBar: MatSnackBar) {}
    method intercept (line 18) | intercept(

FILE: src/app/shared/guards/auth.guard.ts
  class AuthGuard (line 10) | class AuthGuard implements CanActivate {
    method constructor (line 11) | constructor(private router: Router, private authService: AuthService) {}
    method canActivate (line 13) | canActivate(): Observable<boolean> {

FILE: src/app/shared/interfaces/user.interface.ts
  type User (line 1) | interface User {

FILE: src/app/shared/services/auth/auth.service.ts
  type AuthResponse (line 11) | interface AuthResponse {
  class AuthService (line 17) | class AuthService {
    method constructor (line 20) | constructor(private http: HttpClient, private tokenStorage: TokenStora...
    method login (line 22) | login(email: string, password: string): Observable<User> {
    method register (line 34) | register(
    method setUser (line 56) | setUser(user: User | null): void {
    method getUser (line 64) | getUser(): Observable<User | null> {
    method me (line 68) | me(): Observable<User | null> {
    method signOut (line 76) | signOut(): void {
    method getAuthorizationHeaders (line 81) | getAuthorizationHeaders() {
    method checkTheUserOnTheFirstLoad (line 90) | checkTheUserOnTheFirstLoad(): Promise<User | null> {

FILE: src/app/shared/services/auth/token.storage.ts
  class TokenStorage (line 4) | class TokenStorage {
    method signOut (line 7) | signOut(): void {
    method saveToken (line 12) | saveToken(token?: string): void {
    method getToken (line 17) | getToken(): string | null {

FILE: src/app/shared/shared.module.ts
  class SharedModule (line 47) | class SharedModule {}
Condensed preview — 82 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
  {
    "path": ".dockerignore",
    "chars": 36,
    "preview": ".git/\ndist/\nexamples/\nnode_modules/\n"
  },
  {
    "path": ".editorconfig",
    "chars": 245,
    "preview": "# Editor configuration, see http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = "
  },
  {
    "path": ".eslintrc.json",
    "chars": 509,
    "preview": "{\n  \"root\": true,\n  \"ignorePatterns\": [\"projects/**/*\"],\n  \"overrides\": [\n    {\n      \"files\": [\"*.ts\"],\n      \"parserOp"
  },
  {
    "path": ".gitignore",
    "chars": 578,
    "preview": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/dist\n/dist-server\n/tmp\n/ou"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 59,
    "preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn lint-staged\n"
  },
  {
    "path": ".prettierrc",
    "chars": 116,
    "preview": "{\n  \"singleQuote\": true,\n  \"endOfLine\": \"lf\",\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"arrowParens\": \"avoid\"\n}\n"
  },
  {
    "path": "CNAME",
    "chars": 12,
    "preview": "www.mean.io\n"
  },
  {
    "path": "Dockerfile",
    "chars": 207,
    "preview": "FROM node:14.18-alpine\n\nWORKDIR /usr/src/app\nCOPY . /usr/src/app\n\nENV HUSKY_SKIP_INSTALL=true\nRUN yarn --pure-lockfile -"
  },
  {
    "path": "README.md",
    "chars": 1804,
    "preview": "## Welcome to the mean stack\n\nThe mean stack is intended to provide a simple and fun starting point for cloud native ful"
  },
  {
    "path": "_config.yml",
    "chars": 111,
    "preview": "theme: jekyll-theme-minimal\nlogo: https://www.linnovate.net/sites/all/themes/linnovate/images/mean-picture.png\n"
  },
  {
    "path": "_layouts/default.html",
    "chars": 2657,
    "preview": "<!DOCTYPE html>\n<html lang=\"{{ site.lang | default: \"en-US\" }}\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <meta http-equi"
  },
  {
    "path": "angular.json",
    "chars": 2820,
    "preview": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \""
  },
  {
    "path": "assets/css/style.scss",
    "chars": 403,
    "preview": "---\n---\n\n@import \"{{ site.theme }}\";\nh1 {\n  a {\n    color:#00758f;\n    text-size:40px;\n  }\n}\nheader {\n img.logo {\n    ma"
  },
  {
    "path": "docker-compose.yml",
    "chars": 400,
    "preview": "version: '3.8'\n\nservices:\n  app:\n    build: ./\n    image: mean\n    ports:\n      - 4040:4040\n    environment:\n      NODE_"
  },
  {
    "path": "karma.conf.js",
    "chars": 1017,
    "preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
  },
  {
    "path": "lint-staged.config.mjs",
    "chars": 75,
    "preview": "export default {\n  '*.{js,ts,html,scss,md,json}': ['prettier --write'],\n};\n"
  },
  {
    "path": "package.json",
    "chars": 2605,
    "preview": "{\n  \"name\": \"mean\",\n  \"version\": \"2.0.2\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"serve\": \"node server\","
  },
  {
    "path": "protractor.conf.js",
    "chars": 722,
    "preview": "// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/blob/master/lib"
  },
  {
    "path": "server/config/config.js",
    "chars": 1236,
    "preview": "const Joi = require('joi');\n\n// require and configure dotenv, will load vars in .env in PROCESS.ENV\nrequire('dotenv').co"
  },
  {
    "path": "server/config/express.js",
    "chars": 2267,
    "preview": "const path = require('path');\nconst express = require('express');\nconst httpError = require('http-errors');\nconst logger"
  },
  {
    "path": "server/config/mongoose.js",
    "chars": 608,
    "preview": "const mongoose = require('mongoose');\nconst util = require('util');\nconst debug = require('debug')('express-mongoose-es6"
  },
  {
    "path": "server/config/passport.js",
    "chars": 1201,
    "preview": "const passport = require('passport');\nconst LocalStrategy = require('passport-local');\nconst JwtStrategy = require('pass"
  },
  {
    "path": "server/config/swagger.json",
    "chars": 2574,
    "preview": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n      \"version\": \"1.0.0\",\n      \"title\": \"Mean Application API\",\n      \"descriptio"
  },
  {
    "path": "server/controllers/auth.controller.js",
    "chars": 241,
    "preview": "const jwt = require('jsonwebtoken');\nconst config = require('../config/config');\n\nmodule.exports = {\n  generateToken,\n};"
  },
  {
    "path": "server/controllers/user.controller.js",
    "chars": 633,
    "preview": "const bcrypt = require('bcrypt');\nconst Joi = require('joi');\nconst User = require('../models/user.model');\n\nconst userS"
  },
  {
    "path": "server/index.js",
    "chars": 449,
    "preview": "// config should be imported before importing any other file\nconst config = require('./config/config');\nconst app = requ"
  },
  {
    "path": "server/middleware/require-admin.js",
    "chars": 252,
    "preview": "const httpError = require('http-errors');\n\nconst requireAdmin = function (req, res, next) {\n  if (req.user && req.user.r"
  },
  {
    "path": "server/models/user.model.js",
    "chars": 884,
    "preview": "const mongoose = require('mongoose');\n\nconst UserSchema = new mongoose.Schema(\n  {\n    fullname: {\n      type: String,\n "
  },
  {
    "path": "server/routes/auth.route.js",
    "chars": 878,
    "preview": "const express = require('express');\nconst asyncHandler = require('express-async-handler');\nconst passport = require('pas"
  },
  {
    "path": "server/routes/index.route.js",
    "chars": 390,
    "preview": "const express = require('express');\nconst userRoutes = require('./user.route');\nconst authRoutes = require('./auth.route"
  },
  {
    "path": "server/routes/user.route.js",
    "chars": 459,
    "preview": "const express = require('express');\nconst passport = require('passport');\nconst asyncHandler = require('express-async-ha"
  },
  {
    "path": "src/_settings.scss",
    "chars": 212,
    "preview": "/*-----------------------------------------------\n                   Variables\n-----------------------------------------"
  },
  {
    "path": "src/app/admin/admin-routing.module.ts",
    "chars": 521,
    "preview": "import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nimport { AdminCompone"
  },
  {
    "path": "src/app/admin/admin-user-guard.ts",
    "chars": 462,
    "preview": "import { Injectable } from '@angular/core';\nimport { CanActivate } from '@angular/router';\n\nimport { Observable } from '"
  },
  {
    "path": "src/app/admin/admin.component.html",
    "chars": 31,
    "preview": "<h4>HELLO FROM ADMIN PAGE</h4>\n"
  },
  {
    "path": "src/app/admin/admin.component.ts",
    "chars": 157,
    "preview": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-admin',\n  templateUrl: './admin.component.html"
  },
  {
    "path": "src/app/admin/admin.module.ts",
    "chars": 423,
    "preview": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\nimport { AdminRoutingModule }"
  },
  {
    "path": "src/app/app-routing.module.ts",
    "chars": 648,
    "preview": "import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nimport { AuthGuard } "
  },
  {
    "path": "src/app/app.component.html",
    "chars": 134,
    "preview": "<app-header [user]=\"user$ | async\"></app-header>\n<div class=\"wrapper-app\">\n  <router-outlet></router-outlet>\n</div>\n<foo"
  },
  {
    "path": "src/app/app.component.scss",
    "chars": 17,
    "preview": ".wrapper-app {\n}\n"
  },
  {
    "path": "src/app/app.component.ts",
    "chars": 1647,
    "preview": "import { Component } from '@angular/core';\nimport { MatIconRegistry } from '@angular/material/icon';\nimport { DomSanitiz"
  },
  {
    "path": "src/app/app.module.ts",
    "chars": 1429,
    "preview": "import { BrowserAnimationsModule } from '@angular/platform-browser/animations';\nimport { NgModule, APP_INITIALIZER } fro"
  },
  {
    "path": "src/app/auth/auth-routing.module.ts",
    "chars": 575,
    "preview": "import { Routes, RouterModule } from '@angular/router';\n\nimport { LoginComponent } from './login/login.component';\nimpor"
  },
  {
    "path": "src/app/auth/auth.component.scss",
    "chars": 171,
    "preview": ".example-icon {\n  padding: 0 14px;\n}\n\n.example-spacer {\n  flex: 1 1 auto;\n}\n\n.example-card {\n  width: 400px;\n  margin: 1"
  },
  {
    "path": "src/app/auth/auth.module.ts",
    "chars": 426,
    "preview": "import { NgModule } from '@angular/core';\n\nimport { SharedModule } from '../shared/shared.module';\n\nimport { LoginCompon"
  },
  {
    "path": "src/app/auth/login/login.component.html",
    "chars": 1149,
    "preview": "<mat-card class=\"example-card\">\n  <mat-card-header>\n    <mat-card-title>Login</mat-card-title>\n  </mat-card-header>\n  <m"
  },
  {
    "path": "src/app/auth/login/login.component.spec.ts",
    "chars": 645,
    "preview": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { LoginComponent } from './logi"
  },
  {
    "path": "src/app/auth/login/login.component.ts",
    "chars": 578,
    "preview": "import { Component } from '@angular/core';\nimport { Router } from '@angular/router';\n\nimport { AuthService } from '@app/"
  },
  {
    "path": "src/app/auth/register/register.component.html",
    "chars": 2258,
    "preview": "<mat-card class=\"example-card\">\n  <mat-card-header>\n    <mat-card-title>Register</mat-card-title>\n  </mat-card-header>\n "
  },
  {
    "path": "src/app/auth/register/register.component.spec.ts",
    "chars": 666,
    "preview": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { RegisterComponent } from './r"
  },
  {
    "path": "src/app/auth/register/register.component.ts",
    "chars": 1814,
    "preview": "import { Component } from '@angular/core';\nimport { Router } from '@angular/router';\nimport {\n  UntypedFormGroup,\n  Unty"
  },
  {
    "path": "src/app/header/header.component.html",
    "chars": 611,
    "preview": "<header>\n  <mat-toolbar color=\"primary\">\n    <a routerLink=\"/\" class=\"logo\"></a>\n    <span class=\"example-spacer\"></span"
  },
  {
    "path": "src/app/header/header.component.scss",
    "chars": 633,
    "preview": "header {\n  width: 100%;\n  .logo {\n    background-image: url('../../assets/logo.png');\n    width: 50px;\n    height: 50px;"
  },
  {
    "path": "src/app/header/header.component.spec.ts",
    "chars": 652,
    "preview": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { HeaderComponent } from './hea"
  },
  {
    "path": "src/app/header/header.component.ts",
    "chars": 568,
    "preview": "import { Component, Input } from '@angular/core';\nimport { Router } from '@angular/router';\n\nimport { User } from '@app/"
  },
  {
    "path": "src/app/home/home.component.html",
    "chars": 19,
    "preview": "<p>home works!</p>\n"
  },
  {
    "path": "src/app/home/home.component.scss",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/app/home/home.component.spec.ts",
    "chars": 638,
    "preview": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { HomeComponent } from './home."
  },
  {
    "path": "src/app/home/home.component.ts",
    "chars": 194,
    "preview": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-home',\n  templateUrl: './home.component.html',"
  },
  {
    "path": "src/app/interceptors/header.interceptor.ts",
    "chars": 590,
    "preview": "import { Injectable } from '@angular/core';\nimport {\n  HttpEvent,\n  HttpInterceptor,\n  HttpHandler,\n  HttpRequest,\n} fro"
  },
  {
    "path": "src/app/interceptors/http-error.interceptor.ts",
    "chars": 971,
    "preview": "import { Injectable } from '@angular/core';\nimport {\n  HttpEvent,\n  HttpInterceptor,\n  HttpHandler,\n  HttpRequest,\n  Htt"
  },
  {
    "path": "src/app/shared/guards/auth.guard.ts",
    "chars": 638,
    "preview": "import { Injectable } from '@angular/core';\nimport { CanActivate, Router } from '@angular/router';\n\nimport { Observable "
  },
  {
    "path": "src/app/shared/guards/index.ts",
    "chars": 30,
    "preview": "export * from './auth.guard';\n"
  },
  {
    "path": "src/app/shared/interfaces/index.ts",
    "chars": 34,
    "preview": "export * from './user.interface';\n"
  },
  {
    "path": "src/app/shared/interfaces/user.interface.ts",
    "chars": 121,
    "preview": "export interface User {\n  _id: string;\n  fullname: string;\n  createdAt: string;\n  roles: string[];\n  isAdmin: boolean;\n}"
  },
  {
    "path": "src/app/shared/services/auth/auth.service.spec.ts",
    "chars": 363,
    "preview": "import { TestBed, inject } from '@angular/core/testing';\n\nimport { AuthService } from './auth.service';\n\ndescribe('AuthS"
  },
  {
    "path": "src/app/shared/services/auth/auth.service.ts",
    "chars": 2284,
    "preview": "import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\n\nimport { Observable, Beh"
  },
  {
    "path": "src/app/shared/services/auth/token.storage.ts",
    "chars": 436,
    "preview": "import { Injectable } from '@angular/core';\n\n@Injectable({ providedIn: 'root' })\nexport class TokenStorage {\n  private t"
  },
  {
    "path": "src/app/shared/services/index.ts",
    "chars": 37,
    "preview": "export * from './auth/auth.service';\n"
  },
  {
    "path": "src/app/shared/shared.module.ts",
    "chars": 2023,
    "preview": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule, Reactive"
  },
  {
    "path": "src/assets/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/environments/environment.prod.ts",
    "chars": 52,
    "preview": "export const environment = {\n  production: true,\n};\n"
  },
  {
    "path": "src/environments/environment.ts",
    "chars": 53,
    "preview": "export const environment = {\n  production: false,\n};\n"
  },
  {
    "path": "src/index.html",
    "chars": 750,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Mean</title>\n    <base href=\"/\" />\n\n  "
  },
  {
    "path": "src/main.ts",
    "chars": 373,
    "preview": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynami"
  },
  {
    "path": "src/polyfills.ts",
    "chars": 2212,
    "preview": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfi"
  },
  {
    "path": "src/styles.scss",
    "chars": 1022,
    "preview": "/* You can add global styles to this file, and also import other style files */\n@use '@angular/material' as mat;\n@import"
  },
  {
    "path": "src/test.ts",
    "chars": 510,
    "preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/testi"
  },
  {
    "path": "tsconfig.app.json",
    "chars": 243,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"files\": [\n    \"src/polyfills.ts\",\n    \"src/main.ts\"\n  ],\n  \"include\": [\n    \"src/**"
  },
  {
    "path": "tsconfig.json",
    "chars": 469,
    "preview": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"paths\": {\n      \"@app/*\": [\"src/app/*\"]\n    },\n    \"target\": \"ES2022\""
  },
  {
    "path": "tsconfig.spec.json",
    "chars": 285,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"module\": \"commonjs\",\n    \"types\": [\n "
  },
  {
    "path": "tslint.json",
    "chars": 2958,
    "preview": "{\n  \"rulesDirectory\": [\n    \"node_modules/codelyzer\"\n  ],\n  \"rules\": {\n    \"arrow-return-shorthand\": true,\n    \"callable"
  }
]

About this extraction

This page contains the full source code of the linnovate/mean GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 82 files (58.8 KB), approximately 16.9k tokens, and a symbol index with 60 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!