[
  {
    "path": ".dockerignore",
    "content": ".git/\ndist/\nexamples/\nnode_modules/\n"
  },
  {
    "path": ".editorconfig",
    "content": "# Editor configuration, see http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\nmax_line_length = off\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"ignorePatterns\": [\"projects/**/*\"],\n  \"overrides\": [\n    {\n      \"files\": [\"*.ts\"],\n      \"parserOptions\": {\n        \"project\": [\"tsconfig.json\"],\n        \"createDefaultProgram\": true\n      },\n      \"extends\": [\n        \"plugin:@angular-eslint/recommended\",\n        \"plugin:@angular-eslint/template/process-inline-templates\"\n      ],\n      \"rules\": {}\n    },\n    {\n      \"files\": [\"*.html\"],\n      \"extends\": [\"plugin:@angular-eslint/template/recommended\"],\n      \"rules\": {}\n    }\n  ]\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/dist\n/dist-server\n/tmp\n/out-tsc\n\n# dependencies\n/node_modules\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n# misc\n/.angular/cache\n/.sass-cache\n/connect.lock\n/coverage\n/libpeerconnection.log\nnpm-debug.log\nyarn-error.log\ntestem.log\n/typings\n\n# e2e\n/e2e/*.js\n/e2e/*.map\n\n# System Files\n.DS_Store\nThumbs.db\n\n# Env file\n*.env\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn lint-staged\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"singleQuote\": true,\n  \"endOfLine\": \"lf\",\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"arrowParens\": \"avoid\"\n}\n"
  },
  {
    "path": "CNAME",
    "content": "www.mean.io\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:14.18-alpine\n\nWORKDIR /usr/src/app\nCOPY . /usr/src/app\n\nENV HUSKY_SKIP_INSTALL=true\nRUN yarn --pure-lockfile --non-interactive --no-progress\nRUN yarn build:prod\n\nEXPOSE 4040\n\nCMD [\"yarn\", \"serve\"]\n"
  },
  {
    "path": "README.md",
    "content": "## Welcome to the mean stack\n\nThe mean stack is intended to provide a simple and fun starting point for cloud native fullstack javascript applications.  \nMEAN 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:\n\n- **M**ongoDB : Document database – used by your back-end application to store its data as JSON (JavaScript Object Notation) documents\n- **E**xpress (sometimes referred to as Express.js): Back-end web application framework running on top of Node.js\n- **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- **N**ode.js : JavaScript runtime environment – lets you implement your application back-end in JavaScript\n\n### Pre-requisites\n\n- git - [Installation guide](https://www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/) .\n- node.js - [Download page](https://nodejs.org/en/download/) .\n- npm - comes with node or download yarn - [Download page](https://yarnpkg.com/lang/en/docs/install) .\n- mongodb - [Download page](https://www.mongodb.com/download-center/community) .\n\n### Installation\n\n```\ngit clone https://github.com/linnovate/mean\ncd mean\ncp .env.example .env\nyarn\nyarn start (for development)\n```\n\n### Docker based\n\n> ⚠️ Make sure your Docker version is 19.03.0+.\n\n```\ngit clone https://github.com/linnovate/mean\ncd mean\ncp .env.example .env\ndocker-compose up -d\n```\n\n### Credits\n\n- The MEAN name was coined by Valeri Karpov.\n- Initial concept and development were done by Amos Haviv and sponsored by Linnovate.\n- Inspired by the great work of Madhusudhan Srinivasa.\n"
  },
  {
    "path": "_config.yml",
    "content": "theme: jekyll-theme-minimal\nlogo: https://www.linnovate.net/sites/all/themes/linnovate/images/mean-picture.png\n"
  },
  {
    "path": "_layouts/default.html",
    "content": "<!DOCTYPE html>\n<html lang=\"{{ site.lang | default: \"en-US\" }}\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n{% seo %}\n    <link rel=\"stylesheet\" href=\"{{ \"/assets/css/style.css?v=\" | append: site.github.build_revision | relative_url }}\">\n    <!--[if lt IE 9]>\n    <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js\"></script>\n    <![endif]-->\n  </head>\n  <body>\n    <div class=\"wrapper\">\n      <header>\n        {% if site.logo %}\n          <img class=\"logo\"  src=\"{{site.logo | relative_url}}\" alt=\"Logo\" />\n        {% endif %}\n        <p>{{ site.description | default: site.github.project_tagline }}</p>\n\n        {% if site.github.is_project_page %}\n        <p class=\"view\"><a href=\"{{ site.github.repository_url }}\">View the Project on GitHub <small>{{ site.github.repository_nwo }}</small></a></p>\n        {% endif %}\n\n        <!-- Place this tag where you want the button to render. -->\n        <a class=\"github-button\" href=\"https://github.com/linnovate/mean\" data-show-count=\"true\" aria-label=\"Star ntkme/github-buttons on GitHub\">Star</a>\n\n        {% if site.github.is_user_page %}\n        <p class=\"view\"><a href=\"{{ site.github.owner_url }}\">View My GitHub Profile</a></p>\n        {% endif %}\n\n        {% if site.show_downloads %}\n        <ul class=\"downloads\">\n          <li><a href=\"{{ site.github.zip_url }}\">Download <strong>ZIP File</strong></a></li>\n          <li><a href=\"{{ site.github.tar_url }}\">Download <strong>TAR Ball</strong></a></li>\n          <li><a href=\"{{ site.github.repository_url }}\">View On <strong>GitHub</strong></a></li>\n        </ul>\n        {% endif %}\n        <img class=\"ninja\" src=\"/assets/img/ninja.jpg\"/>\n\n      </header>\n      <section>\n\n      {{ content }}\n      {% if site.github.is_project_page %}\n      <p>This project is maintained by <a href=\"{{ site.github.owner_url }}\">{{ site.github.owner_name }}</a></p>\n      {% endif %}\n      </section>\n    </div>\n    <script src=\"{{ \"/assets/js/scale.fix.js\" | relative_url }}\"></script>\n   <!-- Global site tag (gtag.js) - Google Analytics -->\n<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-36499287-4\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n\n // gtag('config', 'UA-XXXXXX-XX'); // change this to your own UA config\n</script>\n\n    <!-- Place this tag in your head or just before your close body tag. -->\n<script async defer src=\"https://buttons.github.io/buttons.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"projects\": {\n    \"mean\": {\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"projectType\": \"application\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:browser\",\n          \"options\": {\n            \"outputPath\": \"dist\",\n            \"index\": \"src/index.html\",\n            \"main\": \"src/main.ts\",\n            \"tsConfig\": \"./tsconfig.app.json\",\n            \"polyfills\": \"src/polyfills.ts\",\n            \"assets\": [\"src/assets\", \"src/favicon.ico\"],\n            \"styles\": [\"src/styles.scss\"],\n            \"scripts\": [],\n            \"aot\": false,\n            \"vendorChunk\": true,\n            \"extractLicenses\": false,\n            \"buildOptimizer\": false,\n            \"sourceMap\": true,\n            \"optimization\": false,\n            \"namedChunks\": true\n          },\n          \"configurations\": {\n            \"production\": {\n              \"optimization\": true,\n              \"outputHashing\": \"all\",\n              \"sourceMap\": false,\n              \"namedChunks\": false,\n              \"aot\": true,\n              \"extractLicenses\": true,\n              \"vendorChunk\": false,\n              \"buildOptimizer\": true,\n              \"fileReplacements\": [\n                {\n                  \"replace\": \"src/environments/environment.ts\",\n                  \"with\": \"src/environments/environment.prod.ts\"\n                }\n              ]\n            }\n          }\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"options\": {\n            \"browserTarget\": \"mean:build\"\n          },\n          \"configurations\": {\n            \"production\": {\n              \"browserTarget\": \"mean:build:production\"\n            }\n          }\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"browserTarget\": \"mean:build\"\n          }\n        },\n        \"test\": {\n          \"builder\": \"@angular-devkit/build-angular:karma\",\n          \"options\": {\n            \"main\": \"src/test.ts\",\n            \"karmaConfig\": \"./karma.conf.js\",\n            \"polyfills\": \"src/polyfills.ts\",\n            \"tsConfig\": \"./tsconfig.spec.json\",\n            \"scripts\": [],\n            \"styles\": [\"src/styles.scss\"],\n            \"assets\": [\"src/assets\", \"src/favicon.ico\"]\n          }\n        },\n        \"lint\": {\n          \"builder\": \"@angular-eslint/builder:lint\",\n          \"options\": {\n            \"lintFilePatterns\": [\"src/**/*.ts\", \"src/**/*.html\"]\n          }\n        }\n      }\n    }\n  },\n  \"schematics\": {\n    \"@schematics/angular:component\": {\n      \"prefix\": \"app\",\n      \"style\": \"scss\"\n    },\n    \"@schematics/angular:directive\": {\n      \"prefix\": \"app\"\n    }\n  }\n}\n"
  },
  {
    "path": "assets/css/style.scss",
    "content": "---\n---\n\n@import \"{{ site.theme }}\";\nh1 {\n  a {\n    color:#00758f;\n    text-size:40px;\n  }\n}\nheader {\n img.logo {\n    margin-left:30px;\n    display:block;\n    height: auto; \n    width: auto; \n    max-width: 150px; \n    max-height: 200px;\n    margin-bottom: 17%;\n }\n img.ninja {\n  margin-top: 20px;\n  height: auto; \n  width: auto; \n  max-width: 350px; \n  max-height: 200px;\n  margin-bottom: 50px;\n }\n\n} \n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3.8'\n\nservices:\n  app:\n    build: ./\n    image: mean\n    ports:\n      - 4040:4040\n    environment:\n      NODE_ENV: production\n      SERVER_PORT: 4040\n      JWT_SECRET: 0a6b944d-d2fb-46fc-a85e-0295c986cd9f\n      MONGO_HOST: mongodb://mongo/mean\n    restart: always\n    depends_on:\n      - mongo\n\n  mongo:\n    image: mongo:4.2\n    volumes:\n      - mongo_data:/data/db\n\nvolumes:\n  mongo_data:\n"
  },
  {
    "path": "karma.conf.js",
    "content": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-file.html\n\nmodule.exports = function (config) {\n  config.set({\n    basePath: '',\n    frameworks: ['jasmine', '@angular-devkit/build-angular'],\n    plugins: [\n      require('karma-jasmine'),\n      require('karma-chrome-launcher'),\n      require('karma-jasmine-html-reporter'),\n      require('karma-coverage-istanbul-reporter'),\n      require('@angular-devkit/build-angular/plugins/karma'),\n    ],\n    client: {\n      clearContext: false, // leave Jasmine Spec Runner output visible in browser\n    },\n    coverageIstanbulReporter: {\n      dir: require('path').join(__dirname, 'coverage'),\n      reports: ['html', 'lcovonly'],\n      fixWebpackSourcePaths: true,\n    },\n    angularCli: {\n      environment: 'dev',\n    },\n    reporters: ['progress', 'kjhtml'],\n    port: 9876,\n    colors: true,\n    logLevel: config.LOG_INFO,\n    autoWatch: true,\n    browsers: ['Chrome'],\n    singleRun: false,\n  });\n};\n"
  },
  {
    "path": "lint-staged.config.mjs",
    "content": "export default {\n  '*.{js,ts,html,scss,md,json}': ['prettier --write'],\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"mean\",\n  \"version\": \"2.0.2\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"serve\": \"node server\",\n    \"start\": \"concurrently -c \\\"yellow.bold,green.bold\\\" -n \\\"SERVER,BUILD\\\" \\\"nodemon server\\\" \\\"ng build --watch\\\"\",\n    \"build:prod\": \"ng build --configuration production\",\n    \"test\": \"ng test\",\n    \"lint\": \"ng lint\",\n    \"e2e\": \"ng e2e\",\n    \"postinstall\": \"husky install\"\n  },\n  \"private\": true,\n  \"dependencies\": {\n    \"@angular/animations\": \"^15.2.9\",\n    \"@angular/cdk\": \"^15.2.9\",\n    \"@angular/common\": \"^15.2.9\",\n    \"@angular/compiler\": \"^15.2.9\",\n    \"@angular/core\": \"^15.2.9\",\n    \"@angular/forms\": \"^15.2.9\",\n    \"@angular/material\": \"^15.2.9\",\n    \"@angular/platform-browser\": \"^15.2.9\",\n    \"@angular/platform-browser-dynamic\": \"^15.2.9\",\n    \"@angular/router\": \"^15.2.9\",\n    \"bcrypt\": \"^5.0.1\",\n    \"body-parser\": \"^1.18.2\",\n    \"compression\": \"^1.7.2\",\n    \"cookie-parser\": \"^1.4.3\",\n    \"cors\": \"^2.8.4\",\n    \"dotenv\": \"^10.0.0\",\n    \"events\": \"^3.0.0\",\n    \"express\": \"^4.16.3\",\n    \"express-async-handler\": \"^1.1.3\",\n    \"express-jwt\": \"^5.3.1\",\n    \"express-validation\": \"^1.0.2\",\n    \"formidable\": \"^3.1.3\",\n    \"helmet\": \"^4.6.0\",\n    \"http-errors\": \"^1.6.3\",\n    \"joi\": \"^17.4.2\",\n    \"jsonwebtoken\": \"^8.2.1\",\n    \"method-override\": \"^3.0.0\",\n    \"mongoose\": \"^6.0.13\",\n    \"morgan\": \"^1.9.1\",\n    \"nodemon\": \"^2.0.15\",\n    \"passport\": \"^0.4.0\",\n    \"passport-jwt\": \"^4.0.0\",\n    \"passport-local\": \"^1.0.0\",\n    \"rxjs\": \"^7.4.0\",\n    \"swagger-ui-express\": \"^4.1.6\",\n    \"zone.js\": \"~0.11.4\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^15.2.9\",\n    \"@angular-eslint/builder\": \"14.0.2\",\n    \"@angular-eslint/eslint-plugin\": \"14.0.2\",\n    \"@angular-eslint/eslint-plugin-template\": \"14.0.2\",\n    \"@angular-eslint/schematics\": \"14.0.2\",\n    \"@angular-eslint/template-parser\": \"14.0.2\",\n    \"@angular/cli\": \"^15.2.9\",\n    \"@angular/compiler-cli\": \"^15.2.9\",\n    \"@angular/language-service\": \"^15.2.9\",\n    \"@types/jasmine\": \"~3.6.0\",\n    \"@types/jasminewd2\": \"~2.0.2\",\n    \"@types/node\": \"~15.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"5.3.0\",\n    \"@typescript-eslint/parser\": \"5.3.0\",\n    \"concurrently\": \"^3.5.1\",\n    \"eslint\": \"^8.2.0\",\n    \"husky\": \"^7.0.2\",\n    \"jasmine-core\": \"~3.6.0\",\n    \"jasmine-spec-reporter\": \"~5.0.0\",\n    \"karma\": \"~6.3.2\",\n    \"karma-chrome-launcher\": \"~3.1.0\",\n    \"karma-coverage-istanbul-reporter\": \"~3.0.2\",\n    \"karma-jasmine\": \"~4.0.0\",\n    \"karma-jasmine-html-reporter\": \"^1.5.0\",\n    \"lint-staged\": \"^13.0.3\",\n    \"prettier\": \"^2.7.1\",\n    \"ts-node\": \"~6.1.0\",\n    \"typescript\": \"4.9.5\"\n  }\n}\n"
  },
  {
    "path": "protractor.conf.js",
    "content": "// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/blob/master/lib/config.ts\n\nconst { SpecReporter } = require('jasmine-spec-reporter');\n\nexports.config = {\n  allScriptsTimeout: 11000,\n  specs: [\n    './e2e/**/*.e2e-spec.ts'\n  ],\n  capabilities: {\n    'browserName': 'chrome'\n  },\n  directConnect: true,\n  baseUrl: 'http://localhost:4200/',\n  framework: 'jasmine',\n  jasmineNodeOpts: {\n    showColors: true,\n    defaultTimeoutInterval: 30000,\n    print: function() {}\n  },\n  onPrepare() {\n    require('ts-node').register({\n      project: 'e2e/tsconfig.e2e.json'\n    });\n    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));\n  }\n};\n"
  },
  {
    "path": "server/config/config.js",
    "content": "const Joi = require('joi');\n\n// require and configure dotenv, will load vars in .env in PROCESS.ENV\nrequire('dotenv').config();\n\n// define validation for all the env vars\nconst envVarsSchema = Joi.object({\n  NODE_ENV: Joi.string()\n    .allow('development', 'production', 'test', 'provision')\n    .default('development'),\n  SERVER_PORT: Joi.number().default(4040),\n  MONGOOSE_DEBUG: Joi.boolean().when('NODE_ENV', {\n    is: Joi.string().equal('development'),\n    then: Joi.boolean().default(true),\n    otherwise: Joi.boolean().default(false),\n  }),\n  JWT_SECRET: Joi.string()\n    .required()\n    .description('JWT Secret required to sign'),\n  MONGO_HOST: Joi.string().required().description('Mongo DB host url'),\n  MONGO_PORT: Joi.number().default(27017),\n})\n  .unknown()\n  .required();\n\nconst { error, value: envVars } = envVarsSchema.validate(process.env);\nif (error) {\n  throw new Error(`Config validation error: ${error.message}`);\n}\n\nconst config = {\n  env: envVars.NODE_ENV,\n  port: envVars.SERVER_PORT,\n  mongooseDebug: envVars.MONGOOSE_DEBUG,\n  jwtSecret: envVars.JWT_SECRET,\n  frontend: envVars.MEAN_FRONTEND || 'angular',\n  mongo: {\n    host: envVars.MONGO_HOST,\n    port: envVars.MONGO_PORT,\n  },\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "server/config/express.js",
    "content": "const path = require('path');\nconst express = require('express');\nconst httpError = require('http-errors');\nconst logger = require('morgan');\nconst bodyParser = require('body-parser');\nconst cookieParser = require('cookie-parser');\nconst compress = require('compression');\nconst methodOverride = require('method-override');\nconst cors = require('cors');\nconst helmet = require('helmet');\nconst swaggerUi = require('swagger-ui-express');\nconst swaggerDocument = require('./swagger.json');\nconst routes = require('../routes/index.route');\nconst config = require('./config');\nconst passport = require('./passport');\n\nconst app = express();\n\nif (config.env === 'development') {\n  app.use(logger('dev'));\n}\n\n// Choose what fronten framework to serve the dist from\nvar distDir = '../../dist/';\nif (config.frontend == 'react') {\n  distDir = '../../node_modules/material-dashboard-react/dist';\n} else {\n  distDir = '../../dist/';\n}\n\n//\napp.use(express.static(path.join(__dirname, distDir)));\napp.use(/^((?!(api)).)*/, (req, res) => {\n  res.sendFile(path.join(__dirname, distDir + '/index.html'));\n});\n\nconsole.log(distDir);\n//React server\napp.use(\n  express.static(\n    path.join(__dirname, '../../node_modules/material-dashboard-react/dist')\n  )\n);\napp.use(/^((?!(api)).)*/, (req, res) => {\n  res.sendFile(path.join(__dirname, '../../dist/index.html'));\n});\n\napp.use(bodyParser.json());\napp.use(bodyParser.urlencoded({ extended: true }));\n\napp.use(cookieParser());\napp.use(compress());\napp.use(methodOverride());\n\n// secure apps by setting various HTTP headers\napp.use(helmet());\n\n// enable CORS - Cross Origin Resource Sharing\napp.use(cors());\n\napp.use(passport.initialize());\n\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));\n\n// API router\napp.use('/api/', routes);\n\n// catch 404 and forward to error handler\napp.use((req, res, next) => {\n  const err = new httpError(404);\n  return next(err);\n});\n\n// error handler, send stacktrace only during development\napp.use((err, req, res, next) => {\n  // customize Joi validation errors\n  if (err.isJoi) {\n    err.message = err.details.map(e => e.message).join('; ');\n    err.status = 400;\n  }\n\n  res.status(err.status || 500).json({\n    message: err.message,\n  });\n  next(err);\n});\n\nmodule.exports = app;\n"
  },
  {
    "path": "server/config/mongoose.js",
    "content": "const mongoose = require('mongoose');\nconst util = require('util');\nconst debug = require('debug')('express-mongoose-es6-rest-api:index');\n\nconst config = require('./config');\n\n// connect to mongo db\nconst mongoUri = config.mongo.host;\nmongoose.connect(mongoUri, { keepAlive: 1 });\nmongoose.connection.on('error', () => {\n  throw new Error(`unable to connect to database: ${mongoUri}`);\n});\n\n// print mongoose logs in dev env\nif (config.MONGOOSE_DEBUG) {\n  mongoose.set('debug', (collectionName, method, query, doc) => {\n    debug(`${collectionName}.${method}`, util.inspect(query, false, 20), doc);\n  });\n}\n"
  },
  {
    "path": "server/config/passport.js",
    "content": "const passport = require('passport');\nconst LocalStrategy = require('passport-local');\nconst JwtStrategy = require('passport-jwt').Strategy;\nconst ExtractJwt = require('passport-jwt').ExtractJwt;\nconst bcrypt = require('bcrypt');\n\nconst User = require('../models/user.model');\nconst config = require('./config');\n\nconst localLogin = new LocalStrategy(\n  {\n    usernameField: 'email',\n  },\n  async (email, password, done) => {\n    let user = await User.findOne({ email });\n    if (!user || !bcrypt.compareSync(password, user.hashedPassword)) {\n      return done(null, false, {\n        error: 'Your login details could not be verified. Please try again.',\n      });\n    }\n    user = user.toObject();\n    delete user.hashedPassword;\n    done(null, user);\n  }\n);\n\nconst jwtLogin = new JwtStrategy(\n  {\n    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n    secretOrKey: config.jwtSecret,\n  },\n  async (payload, done) => {\n    let user = await User.findById(payload._id);\n    if (!user) {\n      return done(null, false);\n    }\n    user = user.toObject();\n    delete user.hashedPassword;\n    done(null, user);\n  }\n);\n\npassport.use(jwtLogin);\npassport.use(localLogin);\n\nmodule.exports = passport;\n"
  },
  {
    "path": "server/config/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n      \"version\": \"1.0.0\",\n      \"title\": \"Mean Application API\",\n      \"description\": \"Mean Application API\",\n      \"license\": {\n        \"name\": \"MIT\",\n        \"url\": \"https://opensource.org/licenses/MIT\"\n      }\n    },\n    \"host\": \"localhost:4040\",\n    \"basePath\": \"/api/\",\n    \"tags\": [\n      {\n        \"name\": \"Users\",\n        \"description\": \"API for users in the system\"\n      },\n      {\n        \"name\": \"Auth\",\n        \"description\": \"API for auth in the system\"\n      }\n    ],\n    \"schemes\": [\n      \"http\"\n    ],\n    \"consumes\": [\n      \"application/json\"\n    ],\n    \"produces\": [\n      \"application/json\"\n    ],\n    \"securityDefinitions\": {\n      \"AuthHeader\": {\n        \"type\": \"apiKey\",\n        \"in\": \"header\",\n        \"name\": \"Authorization\"\n      }\n    },\n    \"paths\": {\n      \"/auth/login\": {\n        \"post\": {\n          \"tags\": [\"Auth\"],\n          \"description\": \"Login to the system\",\n        \"parameters\": [{\n          \"name\": \"auth\",\n          \"in\": \"body\",\n          \"description\": \"User auth details\",\n          \"schema\": {\n            \"type\": \"object\",\n            \"required\": [\"email\", \"password\"],\n            \"properties\": {\n              \"email\": {\n                \"type\": \"string\"\n              },\n              \"password\": {\n                \"type\": \"string\"\n              }\n            }\n          }\n        }],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"User is loggedin\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/User\"\n            }\n          }\n        }\n      }\n      }\n    },\n    \"definitions\": {\n      \"User\": {\n        \"required\": [\n          \"email\",\n          \"fullname\"\n        ],\n        \"properties\": {\n          \"_id\": {\n            \"type\": \"string\",\n            \"uniqueItems\": true\n          },\n          \"email\": {\n            \"type\": \"string\",\n            \"uniqueItems\": true\n          },\n          \"fullname\": {\n            \"type\": \"string\"\n          },\n          \"createdAt\": {\n            \"type\": \"string\"\n          },\n          \"roles\": {\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"Users\": {\n        \"type\": \"array\",\n        \"$ref\": \"#/definitions/User\"\n      },\n      \"Auth\": {\n        \"type\": \"object\",\n        \"properties\": [{\n          \"token\": {\n            \"type\": \"string\"\n          },\n          \"user\": {\n            \"$ref\": \"#/definitions/User\"\n          }\n        }]\n      }\n  }\n}"
  },
  {
    "path": "server/controllers/auth.controller.js",
    "content": "const jwt = require('jsonwebtoken');\nconst config = require('../config/config');\n\nmodule.exports = {\n  generateToken,\n};\n\nfunction generateToken(user) {\n  const payload = JSON.stringify(user);\n  return jwt.sign(payload, config.jwtSecret);\n}\n"
  },
  {
    "path": "server/controllers/user.controller.js",
    "content": "const bcrypt = require('bcrypt');\nconst Joi = require('joi');\nconst User = require('../models/user.model');\n\nconst userSchema = Joi.object({\n  fullname: Joi.string().required(),\n  email: Joi.string().email(),\n  mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/),\n  password: Joi.string().required(),\n  repeatPassword: Joi.string().required().valid(Joi.ref('password')),\n});\n\nmodule.exports = {\n  insert,\n};\n\nasync function insert(user) {\n  user = await userSchema.validateAsync(user, { abortEarly: false });\n  user.hashedPassword = bcrypt.hashSync(user.password, 10);\n  delete user.password;\n  return await new User(user).save();\n}\n"
  },
  {
    "path": "server/index.js",
    "content": "// config should be imported before importing any other file\nconst config = require('./config/config');\nconst app = require('./config/express');\nrequire('./config/mongoose');\n\n// module.parent check is required to support mocha watch\n// src: https://github.com/mochajs/mocha/issues/1912\nif (!module.parent) {\n  app.listen(config.port, () => {\n    console.info(`server started on port ${config.port} (${config.env})`);\n  });\n}\n\nmodule.exports = app;\n"
  },
  {
    "path": "server/middleware/require-admin.js",
    "content": "const httpError = require('http-errors');\n\nconst requireAdmin = function (req, res, next) {\n  if (req.user && req.user.roles.indexOf('admin') > -1) return next();\n  const err = new httpError(401);\n  return next(err);\n};\n\nmodule.exports = requireAdmin;\n"
  },
  {
    "path": "server/models/user.model.js",
    "content": "const mongoose = require('mongoose');\n\nconst UserSchema = new mongoose.Schema(\n  {\n    fullname: {\n      type: String,\n      required: true,\n    },\n    email: {\n      type: String,\n      required: true,\n      unique: true,\n      // Regexp to validate emails with more strict rules as added in tests/users.js which also conforms mostly with RFC2822 guide lines\n      match: [\n        /^(([^<>()[\\]\\\\.,;:\\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,}))$/,\n        'Please enter a valid email',\n      ],\n    },\n    hashedPassword: {\n      type: String,\n      required: true,\n    },\n    createdAt: {\n      type: Date,\n      default: Date.now,\n    },\n    roles: [\n      {\n        type: String,\n      },\n    ],\n  },\n  {\n    versionKey: false,\n  }\n);\n\nmodule.exports = mongoose.model('User', UserSchema);\n"
  },
  {
    "path": "server/routes/auth.route.js",
    "content": "const express = require('express');\nconst asyncHandler = require('express-async-handler');\nconst passport = require('passport');\nconst userCtrl = require('../controllers/user.controller');\nconst authCtrl = require('../controllers/auth.controller');\nconst config = require('../config/config');\n\nconst router = express.Router();\nmodule.exports = router;\n\nrouter.post('/register', asyncHandler(register), login);\nrouter.post(\n  '/login',\n  passport.authenticate('local', { session: false }),\n  login\n);\nrouter.get('/me', passport.authenticate('jwt', { session: false }), login);\n\nasync function register(req, res, next) {\n  let user = await userCtrl.insert(req.body);\n  user = user.toObject();\n  delete user.hashedPassword;\n  req.user = user;\n  next();\n}\n\nfunction login(req, res) {\n  let user = req.user;\n  let token = authCtrl.generateToken(user);\n  res.json({ user, token });\n}\n"
  },
  {
    "path": "server/routes/index.route.js",
    "content": "const express = require('express');\nconst userRoutes = require('./user.route');\nconst authRoutes = require('./auth.route');\n\nconst router = express.Router(); // eslint-disable-line new-cap\n\n/** GET /health-check - Check service health */\nrouter.get('/health-check', (req, res) => res.send('OK'));\n\nrouter.use('/auth', authRoutes);\nrouter.use('/user', userRoutes);\n\nmodule.exports = router;\n"
  },
  {
    "path": "server/routes/user.route.js",
    "content": "const express = require('express');\nconst passport = require('passport');\nconst asyncHandler = require('express-async-handler');\nconst userCtrl = require('../controllers/user.controller');\n\nconst router = express.Router();\nmodule.exports = router;\n\nrouter.use(passport.authenticate('jwt', { session: false }));\n\nrouter.route('/').post(asyncHandler(insert));\n\nasync function insert(req, res) {\n  let user = await userCtrl.insert(req.body);\n  res.json(user);\n}\n"
  },
  {
    "path": "src/_settings.scss",
    "content": "/*-----------------------------------------------\n                   Variables\n-----------------------------------------------*/\n$linesColor: #dbdbdb;\n$categoryTitleColor: #686868;\n$categoryEntityColor: #3f3f3f;\n"
  },
  {
    "path": "src/app/admin/admin-routing.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nimport { AdminComponent } from './admin.component';\nimport { OnlyAdminUsersGuard } from './admin-user-guard';\n\nconst routes: Routes = [\n  {\n    path: 'admin',\n    canActivate: [OnlyAdminUsersGuard],\n    children: [\n      {\n        path: '',\n        component: AdminComponent,\n      },\n    ],\n  },\n];\n\n@NgModule({\n  imports: [RouterModule.forChild(routes)],\n  exports: [RouterModule],\n})\nexport class AdminRoutingModule {}\n"
  },
  {
    "path": "src/app/admin/admin-user-guard.ts",
    "content": "import { Injectable } from '@angular/core';\nimport { CanActivate } from '@angular/router';\n\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\nimport { AuthService } from '@app/shared/services';\n\n@Injectable()\nexport class OnlyAdminUsersGuard implements CanActivate {\n  constructor(private authService: AuthService) {}\n\n  canActivate(): Observable<boolean> {\n    return this.authService.getUser().pipe(map(user => !!user?.isAdmin));\n  }\n}\n"
  },
  {
    "path": "src/app/admin/admin.component.html",
    "content": "<h4>HELLO FROM ADMIN PAGE</h4>\n"
  },
  {
    "path": "src/app/admin/admin.component.ts",
    "content": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-admin',\n  templateUrl: './admin.component.html',\n})\nexport class AdminComponent {}\n"
  },
  {
    "path": "src/app/admin/admin.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\nimport { AdminRoutingModule } from './admin-routing.module';\nimport { AdminComponent } from './admin.component';\nimport { OnlyAdminUsersGuard } from './admin-user-guard';\n\n@NgModule({\n  declarations: [AdminComponent],\n  imports: [CommonModule, AdminRoutingModule],\n  providers: [OnlyAdminUsersGuard],\n})\nexport class AdminModule {}\n"
  },
  {
    "path": "src/app/app-routing.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nimport { AuthGuard } from './shared/guards';\nimport { HomeComponent } from './home/home.component';\n\nconst routes: Routes = [\n  {\n    path: '',\n    component: HomeComponent,\n    canActivate: [AuthGuard],\n  },\n  {\n    path: 'auth',\n    loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule),\n  },\n  {\n    path: 'admin',\n    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),\n  },\n];\n\n@NgModule({\n  imports: [RouterModule.forRoot(routes, {})],\n  exports: [RouterModule],\n})\nexport class AppRoutingModule {}\n"
  },
  {
    "path": "src/app/app.component.html",
    "content": "<app-header [user]=\"user$ | async\"></app-header>\n<div class=\"wrapper-app\">\n  <router-outlet></router-outlet>\n</div>\n<footer></footer>\n"
  },
  {
    "path": "src/app/app.component.scss",
    "content": ".wrapper-app {\n}\n"
  },
  {
    "path": "src/app/app.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { MatIconRegistry } from '@angular/material/icon';\nimport { DomSanitizer } from '@angular/platform-browser';\n\nimport { merge, Observable } from 'rxjs';\n\nimport { User } from './shared/interfaces';\nimport { AuthService } from './shared/services';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html',\n  styleUrls: ['./app.component.scss'],\n})\nexport class AppComponent {\n  user$: Observable<User | null> = merge(\n    // Init on startup\n    this.authService.me(),\n    // Update after login/register/logout\n    this.authService.getUser()\n  );\n\n  constructor(\n    private domSanitizer: DomSanitizer,\n    private matIconRegistry: MatIconRegistry,\n    private authService: AuthService\n  ) {\n    this.registerSvgIcons();\n  }\n\n  registerSvgIcons() {\n    [\n      'close',\n      'add',\n      'add-blue',\n      'airplane-front-view',\n      'air-station',\n      'balloon',\n      'boat',\n      'cargo-ship',\n      'car',\n      'catamaran',\n      'clone',\n      'convertible',\n      'delete',\n      'drone',\n      'fighter-plane',\n      'fire-truck',\n      'horseback-riding',\n      'motorcycle',\n      'railcar',\n      'railroad-train',\n      'rocket-boot',\n      'sailing-boat',\n      'segway',\n      'shuttle',\n      'space-shuttle',\n      'steam-engine',\n      'suv',\n      'tour-bus',\n      'tow-truck',\n      'transportation',\n      'trolleybus',\n      'water-transportation',\n    ].forEach(icon => {\n      this.matIconRegistry.addSvgIcon(\n        icon,\n        this.domSanitizer.bypassSecurityTrustResourceUrl(\n          `assets/icons/${icon}.svg`\n        )\n      );\n    });\n  }\n}\n"
  },
  {
    "path": "src/app/app.module.ts",
    "content": "import { BrowserAnimationsModule } from '@angular/platform-browser/animations';\nimport { NgModule, APP_INITIALIZER } from '@angular/core';\nimport { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';\n\nimport { SharedModule } from './shared/shared.module';\n\nimport { AppComponent } from './app.component';\nimport { AuthHeaderInterceptor } from './interceptors/header.interceptor';\nimport { CatchErrorInterceptor } from './interceptors/http-error.interceptor';\n\nimport { AppRoutingModule } from './app-routing.module';\nimport { HeaderComponent } from './header/header.component';\nimport { HomeComponent } from './home/home.component';\nimport { AuthService } from './shared/services';\n\nexport function appInitializerFactory(authService: AuthService) {\n  return () => authService.checkTheUserOnTheFirstLoad();\n}\n\n@NgModule({\n  imports: [\n    BrowserAnimationsModule,\n    HttpClientModule,\n    SharedModule,\n    AppRoutingModule,\n  ],\n  declarations: [AppComponent, HeaderComponent, HomeComponent],\n  providers: [\n    {\n      provide: HTTP_INTERCEPTORS,\n      useClass: AuthHeaderInterceptor,\n      multi: true,\n    },\n    {\n      provide: HTTP_INTERCEPTORS,\n      useClass: CatchErrorInterceptor,\n      multi: true,\n    },\n    {\n      provide: APP_INITIALIZER,\n      useFactory: appInitializerFactory,\n      multi: true,\n      deps: [AuthService],\n    },\n  ],\n  bootstrap: [AppComponent],\n})\nexport class AppModule {}\n"
  },
  {
    "path": "src/app/auth/auth-routing.module.ts",
    "content": "import { Routes, RouterModule } from '@angular/router';\n\nimport { LoginComponent } from './login/login.component';\nimport { RegisterComponent } from './register/register.component';\n\nconst routes: Routes = [\n  {\n    path: '',\n    children: [\n      {\n        path: '',\n        redirectTo: '/auth/login',\n        pathMatch: 'full',\n      },\n      {\n        path: 'login',\n        component: LoginComponent,\n      },\n      {\n        path: 'register',\n        component: RegisterComponent,\n      },\n    ],\n  },\n];\n\nexport const AuthRoutingModule = RouterModule.forChild(routes);\n"
  },
  {
    "path": "src/app/auth/auth.component.scss",
    "content": ".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: 10% auto;\n}\n\n.mat-card-title {\n  font-size: 16px;\n}\n"
  },
  {
    "path": "src/app/auth/auth.module.ts",
    "content": "import { NgModule } from '@angular/core';\n\nimport { SharedModule } from '../shared/shared.module';\n\nimport { LoginComponent } from './login/login.component';\nimport { RegisterComponent } from './register/register.component';\nimport { AuthRoutingModule } from './auth-routing.module';\n\n@NgModule({\n  imports: [SharedModule, AuthRoutingModule],\n  declarations: [LoginComponent, RegisterComponent],\n})\nexport class AuthModule {}\n"
  },
  {
    "path": "src/app/auth/login/login.component.html",
    "content": "<mat-card class=\"example-card\">\n  <mat-card-header>\n    <mat-card-title>Login</mat-card-title>\n  </mat-card-header>\n  <mat-card-content>\n    <form class=\"example-form\">\n      <table cellspacing=\"0\">\n        <tr>\n          <td>\n            <mat-form-field>\n              <input\n                matInput\n                placeholder=\"Email\"\n                [(ngModel)]=\"email\"\n                name=\"email\"\n                required\n              />\n            </mat-form-field>\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <mat-form-field>\n              <input\n                matInput\n                placeholder=\"Password\"\n                [(ngModel)]=\"password\"\n                type=\"password\"\n                name=\"password\"\n                required\n              />\n            </mat-form-field>\n          </td>\n        </tr>\n      </table>\n    </form>\n  </mat-card-content>\n  <mat-card-actions>\n    <button mat-raised-button (click)=\"login()\" color=\"primary\">Login</button>\n    <span\n      >Don't have an account ?\n      <a [routerLink]=\"['/auth/register']\">register</a> here</span\n    >\n  </mat-card-actions>\n</mat-card>\n"
  },
  {
    "path": "src/app/auth/login/login.component.spec.ts",
    "content": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { LoginComponent } from './login.component';\n\ndescribe('LoginComponent', () => {\n  let component: LoginComponent;\n  let fixture: ComponentFixture<LoginComponent>;\n\n  beforeEach(\n    waitForAsync(() => {\n      TestBed.configureTestingModule({\n        declarations: [LoginComponent],\n      }).compileComponents();\n    })\n  );\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(LoginComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "src/app/auth/login/login.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { Router } from '@angular/router';\n\nimport { AuthService } from '@app/shared/services';\n\n@Component({\n  selector: 'app-login',\n  templateUrl: './login.component.html',\n  styleUrls: ['../auth.component.scss'],\n})\nexport class LoginComponent {\n  email: string | null = null;\n  password: string | null = null;\n\n  constructor(private router: Router, private authService: AuthService) {}\n\n  login(): void {\n    this.authService.login(this.email!, this.password!).subscribe(() => {\n      this.router.navigateByUrl('/');\n    });\n  }\n}\n"
  },
  {
    "path": "src/app/auth/register/register.component.html",
    "content": "<mat-card class=\"example-card\">\n  <mat-card-header>\n    <mat-card-title>Register</mat-card-title>\n  </mat-card-header>\n  <mat-card-content>\n    <form class=\"example-form\">\n      <table cellspacing=\"0\" [formGroup]=\"userForm\">\n        <tr>\n          <td>\n            <mat-form-field>\n              <input\n                matInput\n                placeholder=\"Fullname\"\n                formControlName=\"fullname\"\n                name=\"fullname\"\n                required\n              />\n            </mat-form-field>\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <mat-form-field>\n              <input\n                matInput\n                placeholder=\"Email\"\n                formControlName=\"email\"\n                name=\"email\"\n                required\n              />\n              <mat-error *ngIf=\"email.invalid && email.hasError('email')\"\n                >Invalid email address</mat-error\n              >\n            </mat-form-field>\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <mat-form-field>\n              <input\n                matInput\n                placeholder=\"Password\"\n                formControlName=\"password\"\n                type=\"password\"\n                name=\"password\"\n                required\n              />\n            </mat-form-field>\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <mat-form-field>\n              <input\n                matInput\n                placeholder=\"Repeat Password\"\n                formControlName=\"repeatPassword\"\n                type=\"password\"\n                name=\"repeatPassword\"\n                required\n              />\n              <mat-error\n                *ngIf=\"\n                  repeatPassword.invalid &&\n                  repeatPassword.hasError('passwordMatch')\n                \"\n                >Password mismatch</mat-error\n              >\n            </mat-form-field>\n          </td>\n        </tr>\n      </table>\n    </form>\n  </mat-card-content>\n  <mat-card-actions>\n    <button mat-raised-button (click)=\"register()\" color=\"primary\">\n      Register\n    </button>\n    <span\n      >Already have an account ?\n      <a [routerLink]=\"['/auth/login']\">login</a> here</span\n    >\n  </mat-card-actions>\n</mat-card>\n"
  },
  {
    "path": "src/app/auth/register/register.component.spec.ts",
    "content": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { RegisterComponent } from './register.component';\n\ndescribe('RegisterComponent', () => {\n  let component: RegisterComponent;\n  let fixture: ComponentFixture<RegisterComponent>;\n\n  beforeEach(\n    waitForAsync(() => {\n      TestBed.configureTestingModule({\n        declarations: [RegisterComponent],\n      }).compileComponents();\n    })\n  );\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(RegisterComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "src/app/auth/register/register.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { Router } from '@angular/router';\nimport {\n  UntypedFormGroup,\n  UntypedFormControl,\n  Validators,\n  ValidationErrors,\n  AbstractControl,\n} from '@angular/forms';\n\nimport { AuthService } from '@app/shared/services';\n\n@Component({\n  selector: 'app-register',\n  templateUrl: './register.component.html',\n  styleUrls: ['../auth.component.scss'],\n})\nexport class RegisterComponent {\n  constructor(private router: Router, private authService: AuthService) {}\n\n  passwordsMatchValidator(\n    control: UntypedFormControl\n  ): ValidationErrors | null {\n    const password = control.root.get('password');\n    return password && control.value !== password.value\n      ? {\n          passwordMatch: true,\n        }\n      : null;\n  }\n\n  userForm = new UntypedFormGroup({\n    fullname: new UntypedFormControl('', [Validators.required]),\n    email: new UntypedFormControl('', [Validators.required, Validators.email]),\n    password: new UntypedFormControl('', [Validators.required]),\n    repeatPassword: new UntypedFormControl('', [\n      Validators.required,\n      this.passwordsMatchValidator,\n    ]),\n  });\n\n  get fullname(): AbstractControl {\n    return this.userForm.get('fullname')!;\n  }\n\n  get email(): AbstractControl {\n    return this.userForm.get('email')!;\n  }\n\n  get password(): AbstractControl {\n    return this.userForm.get('password')!;\n  }\n\n  get repeatPassword(): AbstractControl {\n    return this.userForm.get('repeatPassword')!;\n  }\n\n  register(): void {\n    if (this.userForm.invalid) {\n      return;\n    }\n\n    const { fullname, email, password, repeatPassword } =\n      this.userForm.getRawValue();\n\n    this.authService\n      .register(fullname, email, password, repeatPassword)\n      .subscribe(() => {\n        this.router.navigate(['']);\n      });\n  }\n}\n"
  },
  {
    "path": "src/app/header/header.component.html",
    "content": "<header>\n  <mat-toolbar color=\"primary\">\n    <a routerLink=\"/\" class=\"logo\"></a>\n    <span class=\"example-spacer\"></span>\n    <a class=\"links side\" routerLink=\"/auth/login\" *ngIf=\"!user\">Login</a>\n    <div>\n      <a class=\"links side\" *ngIf=\"user\" [matMenuTriggerFor]=\"menu\">\n        <mat-icon>account_circle</mat-icon>{{ user.fullname }}\n      </a>\n      <mat-menu #menu=\"matMenu\">\n        <button mat-menu-item *ngIf=\"user?.isAdmin\" routerLink=\"/admin\">\n          admin\n        </button>\n        <button mat-menu-item (click)=\"logout()\">logout</button>\n      </mat-menu>\n    </div>\n  </mat-toolbar>\n</header>\n"
  },
  {
    "path": "src/app/header/header.component.scss",
    "content": "header {\n  width: 100%;\n  .logo {\n    background-image: url('../../assets/logo.png');\n    width: 50px;\n    height: 50px;\n    background-size: contain;\n    background-repeat: no-repeat;\n  }\n  .example-spacer {\n    flex: 1 1 auto;\n  }\n  .links {\n    color: white;\n    font-family: 'Helvetica Neue', sans-serif;\n    font-size: 15px;\n    font-weight: initial;\n    letter-spacing: -1px;\n    line-height: 1;\n    text-align: center;\n    padding: 15px;\n    &.side {\n      padding: 0 14px;\n    }\n  }\n  .mat-toolbar {\n    background: black;\n  }\n  .mat-icon {\n    vertical-align: middle;\n    margin: 0 5px;\n  }\n  a {\n    cursor: pointer;\n  }\n}\n"
  },
  {
    "path": "src/app/header/header.component.spec.ts",
    "content": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { HeaderComponent } from './header.component';\n\ndescribe('HeaderComponent', () => {\n  let component: HeaderComponent;\n  let fixture: ComponentFixture<HeaderComponent>;\n\n  beforeEach(\n    waitForAsync(() => {\n      TestBed.configureTestingModule({\n        declarations: [HeaderComponent],\n      }).compileComponents();\n    })\n  );\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(HeaderComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "src/app/header/header.component.ts",
    "content": "import { Component, Input } from '@angular/core';\nimport { Router } from '@angular/router';\n\nimport { User } from '@app/shared/interfaces';\n\nimport { AuthService } from '@app/shared/services';\n\n@Component({\n  selector: 'app-header',\n  templateUrl: './header.component.html',\n  styleUrls: ['./header.component.scss'],\n})\nexport class HeaderComponent {\n  @Input() user: User | null = null;\n\n  constructor(private router: Router, private authService: AuthService) {}\n\n  logout(): void {\n    this.authService.signOut();\n    this.router.navigateByUrl('/auth/login');\n  }\n}\n"
  },
  {
    "path": "src/app/home/home.component.html",
    "content": "<p>home works!</p>\n"
  },
  {
    "path": "src/app/home/home.component.scss",
    "content": ""
  },
  {
    "path": "src/app/home/home.component.spec.ts",
    "content": "import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';\n\nimport { HomeComponent } from './home.component';\n\ndescribe('HomeComponent', () => {\n  let component: HomeComponent;\n  let fixture: ComponentFixture<HomeComponent>;\n\n  beforeEach(\n    waitForAsync(() => {\n      TestBed.configureTestingModule({\n        declarations: [HomeComponent],\n      }).compileComponents();\n    })\n  );\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(HomeComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "src/app/home/home.component.ts",
    "content": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-home',\n  templateUrl: './home.component.html',\n  styleUrls: ['./home.component.scss'],\n})\nexport class HomeComponent {}\n"
  },
  {
    "path": "src/app/interceptors/header.interceptor.ts",
    "content": "import { Injectable } from '@angular/core';\nimport {\n  HttpEvent,\n  HttpInterceptor,\n  HttpHandler,\n  HttpRequest,\n} from '@angular/common/http';\n\nimport { Observable } from 'rxjs';\n\nimport { AuthService } from '@app/shared/services';\n\n@Injectable()\nexport class AuthHeaderInterceptor implements HttpInterceptor {\n  constructor(private authService: AuthService) {}\n\n  intercept(\n    req: HttpRequest<any>,\n    next: HttpHandler\n  ): Observable<HttpEvent<any>> {\n    req = req.clone({\n      setHeaders: this.authService.getAuthorizationHeaders(),\n    });\n\n    return next.handle(req);\n  }\n}\n"
  },
  {
    "path": "src/app/interceptors/http-error.interceptor.ts",
    "content": "import { Injectable } from '@angular/core';\nimport {\n  HttpEvent,\n  HttpInterceptor,\n  HttpHandler,\n  HttpRequest,\n  HttpErrorResponse,\n} from '@angular/common/http';\nimport { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';\n\nimport { Observable, throwError } from 'rxjs';\nimport { catchError } from 'rxjs/operators';\n\n@Injectable()\nexport class CatchErrorInterceptor implements HttpInterceptor {\n  constructor(private snackBar: MatSnackBar) {}\n\n  intercept(\n    request: HttpRequest<any>,\n    next: HttpHandler\n  ): Observable<HttpEvent<any>> {\n    return next.handle(request).pipe(catchError(this.showSnackBar));\n  }\n\n  private showSnackBar = (response: HttpErrorResponse): Observable<never> => {\n    const text: string | undefined =\n      response.error?.message ?? response.error.statusText;\n\n    if (text) {\n      this.snackBar.open(text, 'Close', {\n        duration: 2000,\n      });\n    }\n\n    return throwError(() => response);\n  };\n}\n"
  },
  {
    "path": "src/app/shared/guards/auth.guard.ts",
    "content": "import { Injectable } from '@angular/core';\nimport { CanActivate, Router } from '@angular/router';\n\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\nimport { AuthService } from '../services';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthGuard implements CanActivate {\n  constructor(private router: Router, private authService: AuthService) {}\n\n  canActivate(): Observable<boolean> {\n    return this.authService.getUser().pipe(\n      map(user => {\n        if (user !== null) {\n          return true;\n        }\n\n        this.router.navigateByUrl('/auth/login');\n        return false;\n      })\n    );\n  }\n}\n"
  },
  {
    "path": "src/app/shared/guards/index.ts",
    "content": "export * from './auth.guard';\n"
  },
  {
    "path": "src/app/shared/interfaces/index.ts",
    "content": "export * from './user.interface';\n"
  },
  {
    "path": "src/app/shared/interfaces/user.interface.ts",
    "content": "export interface User {\n  _id: string;\n  fullname: string;\n  createdAt: string;\n  roles: string[];\n  isAdmin: boolean;\n}\n"
  },
  {
    "path": "src/app/shared/services/auth/auth.service.spec.ts",
    "content": "import { TestBed, inject } from '@angular/core/testing';\n\nimport { AuthService } from './auth.service';\n\ndescribe('AuthService', () => {\n  beforeEach(() => {\n    TestBed.configureTestingModule({\n      providers: [AuthService],\n    });\n  });\n\n  it('should be created', inject([AuthService], (service: AuthService) => {\n    expect(service).toBeTruthy();\n  }));\n});\n"
  },
  {
    "path": "src/app/shared/services/auth/auth.service.ts",
    "content": "import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\n\nimport { Observable, BehaviorSubject, firstValueFrom, of } from 'rxjs';\nimport { tap, pluck, catchError } from 'rxjs/operators';\n\nimport { User } from '@app/shared/interfaces';\n\nimport { TokenStorage } from './token.storage';\n\ninterface AuthResponse {\n  token: string;\n  user: User;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n  private user$ = new BehaviorSubject<User | null>(null);\n\n  constructor(private http: HttpClient, private tokenStorage: TokenStorage) {}\n\n  login(email: string, password: string): Observable<User> {\n    return this.http\n      .post<AuthResponse>('/api/auth/login', { email, password })\n      .pipe(\n        tap(({ token, user }) => {\n          this.setUser(user);\n          this.tokenStorage.saveToken(token);\n        }),\n        pluck('user')\n      );\n  }\n\n  register(\n    fullname: string,\n    email: string,\n    password: string,\n    repeatPassword: string\n  ): Observable<User> {\n    return this.http\n      .post<AuthResponse>('/api/auth/register', {\n        fullname,\n        email,\n        password,\n        repeatPassword,\n      })\n      .pipe(\n        tap(({ token, user }) => {\n          this.setUser(user);\n          this.tokenStorage.saveToken(token);\n        }),\n        pluck('user')\n      );\n  }\n\n  setUser(user: User | null): void {\n    if (user) {\n      user.isAdmin = user.roles.includes('admin');\n    }\n\n    this.user$.next(user);\n  }\n\n  getUser(): Observable<User | null> {\n    return this.user$.asObservable();\n  }\n\n  me(): Observable<User | null> {\n    return this.http.get<AuthResponse>('/api/auth/me').pipe(\n      tap(({ user }) => this.setUser(user)),\n      pluck('user'),\n      catchError(() => of(null))\n    );\n  }\n\n  signOut(): void {\n    this.tokenStorage.signOut();\n    this.setUser(null);\n  }\n\n  getAuthorizationHeaders() {\n    const token: string | null = this.tokenStorage.getToken() || '';\n    return { Authorization: `Bearer ${token}` };\n  }\n\n  /**\n   * Let's try to get user's information if he was logged in previously,\n   * thus we can ensure that the user is able to access the `/` (home) page.\n   */\n  checkTheUserOnTheFirstLoad(): Promise<User | null> {\n    return firstValueFrom(this.me());\n  }\n}\n"
  },
  {
    "path": "src/app/shared/services/auth/token.storage.ts",
    "content": "import { Injectable } from '@angular/core';\n\n@Injectable({ providedIn: 'root' })\nexport class TokenStorage {\n  private tokenKey = 'authToken';\n\n  signOut(): void {\n    localStorage.removeItem(this.tokenKey);\n    localStorage.clear();\n  }\n\n  saveToken(token?: string): void {\n    if (!token) return;\n    localStorage.setItem(this.tokenKey, token);\n  }\n\n  getToken(): string | null {\n    return localStorage.getItem(this.tokenKey);\n  }\n}\n"
  },
  {
    "path": "src/app/shared/services/index.ts",
    "content": "export * from './auth/auth.service';\n"
  },
  {
    "path": "src/app/shared/shared.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule, ReactiveFormsModule } from '@angular/forms';\n\nimport { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';\nimport { MatLegacyTabsModule as MatTabsModule } from '@angular/material/legacy-tabs';\nimport { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';\nimport { MatLegacyListModule as MatListModule } from '@angular/material/legacy-list';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatTreeModule } from '@angular/material/tree';\nimport { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';\nimport { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';\nimport { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';\nimport { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';\nimport { MatDividerModule } from '@angular/material/divider';\nimport { MatToolbarModule } from '@angular/material/toolbar';\nimport { MatSidenavModule } from '@angular/material/sidenav';\nimport { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/legacy-snack-bar';\nimport { MatExpansionModule } from '@angular/material/expansion';\nimport { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';\nimport { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar';\n\n@NgModule({\n  exports: [\n    FormsModule,\n    ReactiveFormsModule,\n    CommonModule,\n    MatMenuModule,\n    MatTabsModule,\n    MatCardModule,\n    MatListModule,\n    MatIconModule,\n    MatTreeModule,\n    MatInputModule,\n    MatSelectModule,\n    MatDialogModule,\n    MatButtonModule,\n    MatDividerModule,\n    MatToolbarModule,\n    MatSidenavModule,\n    MatSnackBarModule,\n    MatExpansionModule,\n    MatFormFieldModule,\n    MatProgressBarModule,\n  ],\n})\nexport class SharedModule {}\n"
  },
  {
    "path": "src/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "src/environments/environment.prod.ts",
    "content": "export const environment = {\n  production: true,\n};\n"
  },
  {
    "path": "src/environments/environment.ts",
    "content": "export const environment = {\n  production: false,\n};\n"
  },
  {
    "path": "src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Mean</title>\n    <base href=\"/\" />\n\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n\n    <link\n      rel=\"prefetch\"\n      href=\"https://fonts.googleapis.com/icon?family=Material+Icons\"\n      as=\"style\"\n      crossorigin\n    />\n    <link\n      href=\"https://fonts.googleapis.com/icon?family=Material+Icons\"\n      rel=\"stylesheet\"\n    />\n\n    <link\n      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\"\n      rel=\"stylesheet\"\n    />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "src/main.ts",
    "content": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app/app.module';\nimport { environment } from './environments/environment';\n\nif (environment.production) {\n  enableProdMode();\n}\n\nplatformBrowserDynamic()\n  .bootstrapModule(AppModule)\n  .catch(err => console.log(err));\n"
  },
  {
    "path": "src/polyfills.ts",
    "content": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfills to this file.\n *\n * This file is divided into 2 sections:\n *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.\n *   2. Application imports. Files imported after ZoneJS that should be loaded before your main\n *      file.\n *\n * The current setup is for so-called \"evergreen\" browsers; the last versions of browsers that\n * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),\n * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.\n *\n * Learn more in https://angular.io/guide/browser-support\n */\n\n/***************************************************************************************************\n * BROWSER POLYFILLS\n */\n\n/**\n * By default, zone.js will patch all possible macroTask and DomEvents\n * user can disable parts of macroTask/DomEvents patch by setting following flags\n * because those flags need to be set before `zone.js` being loaded, and webpack\n * will put import in the top of bundle, so user need to create a separate file\n * in this directory (for example: zone-flags.ts), and put the following flags\n * into that file, and then add the following code before importing zone.js.\n * import './zone-flags.ts';\n *\n * The flags allowed in zone-flags.ts are listed here.\n *\n * The following flags will work for all browsers.\n *\n * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame\n * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick\n * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames\n *\n *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js\n *  with the following flag, it will bypass `zone.js` patch for IE/Edge\n *\n *  (window as any).__Zone_enable_cross_context_check = true;\n *\n */\n\n/***************************************************************************************************\n * Zone JS is required by default for Angular itself.\n */\nimport 'zone.js'; // Included with Angular CLI.\n"
  },
  {
    "path": "src/styles.scss",
    "content": "/* You can add global styles to this file, and also import other style files */\n@use '@angular/material' as mat;\n@import '@angular/material/prebuilt-themes/deeppurple-amber.css';\n\n$custom-typography: mat.define-legacy-typography-config(\n  $font-family: 'Exo',\n);\n\n// TODO(v15): As of v15 mat.legacy-core no longer includes default typography styles.\n//  The following line adds:\n//    1. Default typography styles for all components\n//    2. Styles for typography hierarchy classes (e.g. .mat-headline-1)\n//  If you specify typography styles for the components you use elsewhere, you should delete this line.\n//  If you don't need the default component typographies but still want the hierarchy styles,\n//  you can delete this line and instead use:\n//    `@include mat.legacy-typography-hierarchy($custom-typography);`\n@include mat.all-legacy-component-typographies($custom-typography);\n@include mat.legacy-core();\n\nbody {\n  background-color: #f4f3f3;\n  margin: 0;\n  app-root * {\n    font-family: 'Exo', sans-serif;\n  }\n}\n"
  },
  {
    "path": "src/test.ts",
    "content": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/testing';\nimport { getTestBed } from '@angular/core/testing';\nimport {\n  BrowserDynamicTestingModule,\n  platformBrowserDynamicTesting,\n} from '@angular/platform-browser-dynamic/testing';\n\n// First, initialize the Angular testing environment.\ngetTestBed().initTestEnvironment(\n  BrowserDynamicTestingModule,\n  platformBrowserDynamicTesting(),\n  {\n    teardown: { destroyAfterEach: true },\n  }\n);\n"
  },
  {
    "path": "tsconfig.app.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"files\": [\n    \"src/polyfills.ts\",\n    \"src/main.ts\"\n  ],\n  \"include\": [\n    \"src/**/*.d.ts\"\n  ],\n  \"angularCompilerOptions\": {\n    \"fullTemplateTypeCheck\": true,\n    \"strictInjectionParameters\": true\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"paths\": {\n      \"@app/*\": [\"src/app/*\"]\n    },\n    \"target\": \"ES2022\",\n    \"typeRoots\": [\"node_modules/@types\"],\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"module\": \"es2020\",\n    \"moduleResolution\": \"node\",\n    \"strict\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"importHelpers\": true,\n    \"experimentalDecorators\": true,\n    \"suppressImplicitAnyIndexErrors\": true,\n    \"useDefineForClassFields\": false\n  }\n}\n"
  },
  {
    "path": "tsconfig.spec.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"module\": \"commonjs\",\n    \"types\": [\n      \"jasmine\",\n      \"node\"\n    ]\n  },\n  \"files\": [\n    \"src/test.ts\",\n    \"src/polyfills.ts\"\n  ],\n  \"include\": [\n    \"src/**/*.spec.ts\",\n    \"src/**/*.d.ts\"\n  ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"rulesDirectory\": [\n    \"node_modules/codelyzer\"\n  ],\n  \"rules\": {\n    \"arrow-return-shorthand\": true,\n    \"callable-types\": true,\n    \"class-name\": true,\n    \"comment-format\": [\n      true,\n      \"check-space\"\n    ],\n    \"curly\": true,\n    \"deprecation\": {\n      \"severity\": \"warn\"\n    },\n    \"eofline\": true,\n    \"forin\": true,\n    \"import-blacklist\": [\n      true,\n      \"rxjs/Rx\"\n    ],\n    \"import-spacing\": true,\n    \"indent\": [\n      true,\n      \"spaces\"\n    ],\n    \"interface-over-type-literal\": true,\n    \"label-position\": true,\n    \"max-line-length\": [\n      true,\n      140\n    ],\n    \"member-access\": false,\n    \"member-ordering\": [\n      true,\n      {\n        \"order\": [\n          \"static-field\",\n          \"instance-field\",\n          \"static-method\",\n          \"instance-method\"\n        ]\n      }\n    ],\n    \"no-arg\": true,\n    \"no-bitwise\": true,\n    \"no-console\": [\n      true,\n      \"debug\",\n      \"info\",\n      \"time\",\n      \"timeEnd\",\n      \"trace\"\n    ],\n    \"no-construct\": true,\n    \"no-debugger\": true,\n    \"no-duplicate-super\": true,\n    \"no-empty\": false,\n    \"no-empty-interface\": true,\n    \"no-eval\": true,\n    \"no-inferrable-types\": [\n      true,\n      \"ignore-params\"\n    ],\n    \"no-misused-new\": true,\n    \"no-non-null-assertion\": true,\n    \"no-shadowed-variable\": true,\n    \"no-string-literal\": false,\n    \"no-string-throw\": true,\n    \"no-switch-case-fall-through\": true,\n    \"no-trailing-whitespace\": true,\n    \"no-unnecessary-initializer\": true,\n    \"no-unused-expression\": true,\n    \"no-var-keyword\": true,\n    \"object-literal-sort-keys\": false,\n    \"one-line\": [\n      true,\n      \"check-open-brace\",\n      \"check-catch\",\n      \"check-else\",\n      \"check-whitespace\"\n    ],\n    \"prefer-const\": true,\n    \"quotemark\": [\n      true,\n      \"single\"\n    ],\n    \"radix\": true,\n    \"semicolon\": [\n      true,\n      \"always\"\n    ],\n    \"triple-equals\": [\n      true,\n      \"allow-null-check\"\n    ],\n    \"typedef-whitespace\": [\n      true,\n      {\n        \"call-signature\": \"nospace\",\n        \"index-signature\": \"nospace\",\n        \"parameter\": \"nospace\",\n        \"property-declaration\": \"nospace\",\n        \"variable-declaration\": \"nospace\"\n      }\n    ],\n    \"unified-signatures\": true,\n    \"variable-name\": false,\n    \"whitespace\": [\n      true,\n      \"check-branch\",\n      \"check-decl\",\n      \"check-operator\",\n      \"check-separator\",\n      \"check-type\"\n    ],\n    \"directive-selector\": [\n      true,\n      \"attribute\",\n      \"app\",\n      \"camelCase\"\n    ],\n    \"component-selector\": [\n      true,\n      \"element\",\n      \"app\",\n      \"kebab-case\"\n    ],\n    \"no-output-on-prefix\": true,\n    \"no-inputs-metadata-property\": true,\n    \"no-outputs-metadata-property\": true,\n    \"no-host-metadata-property\": true,\n    \"no-input-rename\": true,\n    \"no-output-rename\": true,\n    \"use-lifecycle-interface\": true,\n    \"use-pipe-transform-interface\": true,\n    \"component-class-suffix\": true,\n    \"directive-class-suffix\": true\n  }\n}\n"
  }
]